|
|
|
@ -0,0 +1,181 @@
|
|
|
|
|
/*
|
|
|
|
|
* 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 <gmock/gmock.h>
|
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
#include <health/utils.h>
|
|
|
|
|
|
|
|
|
|
#include "healthd_mode_charger.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 Charger {
|
|
|
|
|
public:
|
|
|
|
|
// Inherit constructor.
|
|
|
|
|
using Charger::Charger;
|
|
|
|
|
// Expose protected functions to be used in tests.
|
|
|
|
|
void Init(struct healthd_config* config) override { Charger::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
|