[Lldb-commits] [lldb] 49f55b0 - [lldb][Android] Add PlatformAndroidTest
Kazuki Sakamoto via lldb-commits
lldb-commits at lists.llvm.org
Tue Jun 20 16:16:17 PDT 2023
Author: Kazuki Sakamoto
Date: 2023-06-20T16:15:02-07:00
New Revision: 49f55b025d81823fa7e2b287c8930a8304483e5a
URL: https://github.com/llvm/llvm-project/commit/49f55b025d81823fa7e2b287c8930a8304483e5a
DIFF: https://github.com/llvm/llvm-project/commit/49f55b025d81823fa7e2b287c8930a8304483e5a.diff
LOG: [lldb][Android] Add PlatformAndroidTest
To test D152759 [lldb][Android] Support zip .so file
introduce PlatformAndroidTest with the capability of mocking adb client.
Differential Revision: https://reviews.llvm.org/D152855
Added:
lldb/unittests/Platform/Android/PlatformAndroidTest.cpp
Modified:
lldb/source/Plugins/Platform/Android/AdbClient.h
lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
lldb/source/Plugins/Platform/Android/PlatformAndroid.h
lldb/unittests/Platform/Android/CMakeLists.txt
Removed:
################################################################################
diff --git a/lldb/source/Plugins/Platform/Android/AdbClient.h b/lldb/source/Plugins/Platform/Android/AdbClient.h
index 04ad3819f57f8..851c09957bd4a 100644
--- a/lldb/source/Plugins/Platform/Android/AdbClient.h
+++ b/lldb/source/Plugins/Platform/Android/AdbClient.h
@@ -36,20 +36,22 @@ class AdbClient {
friend class AdbClient;
public:
- ~SyncService();
+ virtual ~SyncService();
- Status PullFile(const FileSpec &remote_file, const FileSpec &local_file);
+ virtual Status PullFile(const FileSpec &remote_file,
+ const FileSpec &local_file);
Status PushFile(const FileSpec &local_file, const FileSpec &remote_file);
- Status Stat(const FileSpec &remote_file, uint32_t &mode, uint32_t &size,
- uint32_t &mtime);
+ virtual Status Stat(const FileSpec &remote_file, uint32_t &mode,
+ uint32_t &size, uint32_t &mtime);
bool IsConnected() const;
- private:
+ protected:
explicit SyncService(std::unique_ptr<Connection> &&conn);
+ private:
Status SendSyncRequest(const char *request_id, const uint32_t data_len,
const void *data);
@@ -78,7 +80,7 @@ class AdbClient {
AdbClient();
explicit AdbClient(const std::string &device_id);
- ~AdbClient();
+ virtual ~AdbClient();
const std::string &GetDeviceID() const;
@@ -96,10 +98,11 @@ class AdbClient {
Status Shell(const char *command, std::chrono::milliseconds timeout,
std::string *output);
- Status ShellToFile(const char *command, std::chrono::milliseconds timeout,
- const FileSpec &output_file_spec);
+ virtual Status ShellToFile(const char *command,
+ std::chrono::milliseconds timeout,
+ const FileSpec &output_file_spec);
- std::unique_ptr<SyncService> GetSyncService(Status &error);
+ virtual std::unique_ptr<SyncService> GetSyncService(Status &error);
Status SwitchDeviceTransport();
diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
index 4b09e8b253412..36ca784d5ebf1 100644
--- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
+++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
@@ -201,12 +201,14 @@ Status PlatformAndroid::GetFile(const FileSpec &source,
// mode == 0 can signify that adbd cannot access the file due security
// constraints - try "cat ..." as a fallback.
- AdbClient adb(m_device_id);
+ AdbClientUP adb(GetAdbClient(error));
+ if (error.Fail())
+ return error;
char cmd[PATH_MAX];
snprintf(cmd, sizeof(cmd), "cat '%s'", source_file.c_str());
- return adb.ShellToFile(cmd, minutes(1), destination);
+ return adb->ShellToFile(cmd, minutes(1), destination);
}
Status PlatformAndroid::PutFile(const FileSpec &source,
@@ -250,7 +252,10 @@ Status PlatformAndroid::DownloadModuleSlice(const FileSpec &src_file_spec,
if (pos != std::string::npos)
source_file = source_file.substr(0, pos);
- AdbClient adb(m_device_id);
+ Status error;
+ AdbClientUP adb(GetAdbClient(error));
+ if (error.Fail())
+ return error;
// Use 'shell dd' to download the file slice with the offset and size.
char cmd[PATH_MAX];
@@ -259,7 +264,7 @@ Status PlatformAndroid::DownloadModuleSlice(const FileSpec &src_file_spec,
"skip=%" PRIu64 " count=%" PRIu64 " status=none",
source_file.c_str(), src_offset, src_size);
- return adb.ShellToFile(cmd, minutes(1), dst_file_spec);
+ return adb->ShellToFile(cmd, minutes(1), dst_file_spec);
}
Status PlatformAndroid::DisconnectRemote() {
@@ -283,9 +288,12 @@ uint32_t PlatformAndroid::GetSdkVersion() {
return m_sdk_version;
std::string version_string;
- AdbClient adb(m_device_id);
- Status error =
- adb.Shell("getprop ro.build.version.sdk", seconds(5), &version_string);
+ Status error;
+ AdbClientUP adb(GetAdbClient(error));
+ if (error.Fail())
+ return 0;
+ error =
+ adb->Shell("getprop ro.build.version.sdk", seconds(5), &version_string);
version_string = llvm::StringRef(version_string).trim().str();
if (error.Fail() || version_string.empty()) {
@@ -321,10 +329,13 @@ Status PlatformAndroid::DownloadSymbolFile(const lldb::ModuleSP &module_sp,
nullptr)
return Status("Symtab already available in the module");
- AdbClient adb(m_device_id);
+ Status error;
+ AdbClientUP adb(GetAdbClient(error));
+ if (error.Fail())
+ return error;
std::string tmpdir;
- Status error = adb.Shell("mktemp --directory --tmpdir /data/local/tmp",
- seconds(5), &tmpdir);
+ error = adb->Shell("mktemp --directory --tmpdir /data/local/tmp", seconds(5),
+ &tmpdir);
if (error.Fail() || tmpdir.empty())
return Status("Failed to generate temporary directory on the device (%s)",
error.AsCString());
@@ -335,7 +346,7 @@ Status PlatformAndroid::DownloadSymbolFile(const lldb::ModuleSP &module_sp,
tmpdir_remover(&tmpdir, [&adb](std::string *s) {
StreamString command;
command.Printf("rm -rf %s", s->c_str());
- Status error = adb.Shell(command.GetData(), seconds(5), nullptr);
+ Status error = adb->Shell(command.GetData(), seconds(5), nullptr);
Log *log = GetLog(LLDBLog::Platform);
if (log && error.Fail())
@@ -351,7 +362,7 @@ Status PlatformAndroid::DownloadSymbolFile(const lldb::ModuleSP &module_sp,
command.Printf("oatdump --symbolize=%s --output=%s",
module_sp->GetPlatformFileSpec().GetPath(false).c_str(),
symfile_platform_filespec.GetPath(false).c_str());
- error = adb.Shell(command.GetData(), minutes(1), nullptr);
+ error = adb->Shell(command.GetData(), minutes(1), nullptr);
if (error.Fail())
return Status("Oatdump failed: %s", error.AsCString());
@@ -390,11 +401,22 @@ PlatformAndroid::GetLibdlFunctionDeclarations(lldb_private::Process *process) {
return PlatformPOSIX::GetLibdlFunctionDeclarations(process);
}
+PlatformAndroid::AdbClientUP PlatformAndroid::GetAdbClient(Status &error) {
+ AdbClientUP adb(std::make_unique<AdbClient>(m_device_id));
+ if (adb)
+ error.Clear();
+ else
+ error = Status("Failed to create AdbClient");
+ return adb;
+}
+
AdbClient::SyncService *PlatformAndroid::GetSyncService(Status &error) {
if (m_adb_sync_svc && m_adb_sync_svc->IsConnected())
return m_adb_sync_svc.get();
- AdbClient adb(m_device_id);
- m_adb_sync_svc = adb.GetSyncService(error);
+ AdbClientUP adb(GetAdbClient(error));
+ if (error.Fail())
+ return nullptr;
+ m_adb_sync_svc = adb->GetSyncService(error);
return (error.Success()) ? m_adb_sync_svc.get() : nullptr;
}
diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h
index 1a66ddebb2438..2da3a985c70c3 100644
--- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h
+++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h
@@ -70,6 +70,9 @@ class PlatformAndroid : public platform_linux::PlatformLinux {
llvm::StringRef
GetLibdlFunctionDeclarations(lldb_private::Process *process) override;
+ typedef std::unique_ptr<AdbClient> AdbClientUP;
+ virtual AdbClientUP GetAdbClient(Status &error);
+
private:
AdbClient::SyncService *GetSyncService(Status &error);
@@ -78,7 +81,7 @@ class PlatformAndroid : public platform_linux::PlatformLinux {
uint32_t m_sdk_version;
};
-} // namespace platofor_android
+} // namespace platform_android
} // namespace lldb_private
#endif // LLDB_SOURCE_PLUGINS_PLATFORM_ANDROID_PLATFORMANDROID_H
diff --git a/lldb/unittests/Platform/Android/CMakeLists.txt b/lldb/unittests/Platform/Android/CMakeLists.txt
index 489fdab7036c8..df6a95576f562 100644
--- a/lldb/unittests/Platform/Android/CMakeLists.txt
+++ b/lldb/unittests/Platform/Android/CMakeLists.txt
@@ -2,6 +2,7 @@ include_directories(${LLDB_SOURCE_DIR}/source/Plugins/Platform/Android)
add_lldb_unittest(AdbClientTests
AdbClientTest.cpp
+ PlatformAndroidTest.cpp
LINK_LIBS
lldbPluginPlatformAndroid
diff --git a/lldb/unittests/Platform/Android/PlatformAndroidTest.cpp b/lldb/unittests/Platform/Android/PlatformAndroidTest.cpp
new file mode 100644
index 0000000000000..1d548c5e59c0e
--- /dev/null
+++ b/lldb/unittests/Platform/Android/PlatformAndroidTest.cpp
@@ -0,0 +1,166 @@
+//===-- PlatformAndroidTest.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/Platform/Android/PlatformAndroid.h"
+#include "Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h"
+#include "TestingSupport/SubsystemRAII.h"
+#include "TestingSupport/TestUtilities.h"
+#include "lldb/Utility/Connection.h"
+#include "gmock/gmock.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::platform_android;
+using namespace testing;
+
+namespace {
+
+class MockSyncService : public AdbClient::SyncService {
+public:
+ MockSyncService() : SyncService(std::unique_ptr<Connection>()) {}
+
+ MOCK_METHOD2(PullFile,
+ Status(const FileSpec &remote_file, const FileSpec &local_file));
+ MOCK_METHOD4(Stat, Status(const FileSpec &remote_file, uint32_t &mode,
+ uint32_t &size, uint32_t &mtime));
+};
+
+typedef std::unique_ptr<AdbClient::SyncService> SyncServiceUP;
+
+class MockAdbClient : public AdbClient {
+public:
+ explicit MockAdbClient() : AdbClient("mock") {}
+
+ MOCK_METHOD3(ShellToFile,
+ Status(const char *command, std::chrono::milliseconds timeout,
+ const FileSpec &output_file_spec));
+ MOCK_METHOD1(GetSyncService, SyncServiceUP(Status &error));
+};
+
+class PlatformAndroidTest : public PlatformAndroid, public ::testing::Test {
+public:
+ PlatformAndroidTest() : PlatformAndroid(false) {
+ m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer());
+ }
+
+ MOCK_METHOD1(GetAdbClient, AdbClientUP(Status &error));
+};
+
+} // namespace
+
+TEST_F(PlatformAndroidTest, DownloadModuleSliceWithAdbClientError) {
+ EXPECT_CALL(*this, GetAdbClient(_))
+ .Times(1)
+ .WillOnce(DoAll(WithArg<0>([](auto &arg) {
+ arg = Status("Failed to create AdbClient");
+ }),
+ Return(ByMove(AdbClientUP()))));
+
+ EXPECT_TRUE(
+ DownloadModuleSlice(
+ FileSpec("/system/app/Test/Test.apk!/lib/arm64-v8a/libtest.so"), 4096,
+ 3600, FileSpec())
+ .Fail());
+}
+
+TEST_F(PlatformAndroidTest, DownloadModuleSliceWithNormalFile) {
+ auto sync_service = new MockSyncService();
+ EXPECT_CALL(*sync_service, Stat(FileSpec("/system/lib64/libc.so"), _, _, _))
+ .Times(1)
+ .WillOnce(DoAll(SetArgReferee<1>(1), Return(Status())));
+ EXPECT_CALL(*sync_service, PullFile(FileSpec("/system/lib64/libc.so"), _))
+ .Times(1)
+ .WillOnce(Return(Status()));
+
+ auto adb_client = new MockAdbClient();
+ EXPECT_CALL(*adb_client, GetSyncService(_))
+ .Times(1)
+ .WillOnce(Return(ByMove(SyncServiceUP(sync_service))));
+
+ EXPECT_CALL(*this, GetAdbClient(_))
+ .Times(1)
+ .WillOnce(Return(ByMove(AdbClientUP(adb_client))));
+
+ EXPECT_TRUE(
+ DownloadModuleSlice(FileSpec("/system/lib64/libc.so"), 0, 0, FileSpec())
+ .Success());
+}
+
+TEST_F(PlatformAndroidTest, DownloadModuleSliceWithZipFile) {
+ auto adb_client = new MockAdbClient();
+ EXPECT_CALL(*adb_client,
+ ShellToFile(StrEq("dd if='/system/app/Test/Test.apk' "
+ "iflag=skip_bytes,count_bytes "
+ "skip=4096 count=3600 status=none"),
+ _, _))
+ .Times(1)
+ .WillOnce(Return(Status()));
+
+ EXPECT_CALL(*this, GetAdbClient(_))
+ .Times(1)
+ .WillOnce(Return(ByMove(AdbClientUP(adb_client))));
+
+ EXPECT_TRUE(
+ DownloadModuleSlice(
+ FileSpec("/system/app/Test/Test.apk!/lib/arm64-v8a/libtest.so"), 4096,
+ 3600, FileSpec())
+ .Success());
+}
+
+TEST_F(PlatformAndroidTest, GetFileWithNormalFile) {
+ auto sync_service = new MockSyncService();
+ EXPECT_CALL(*sync_service, Stat(FileSpec("/data/local/tmp/test"), _, _, _))
+ .Times(1)
+ .WillOnce(DoAll(SetArgReferee<1>(1), Return(Status())));
+ EXPECT_CALL(*sync_service, PullFile(FileSpec("/data/local/tmp/test"), _))
+ .Times(1)
+ .WillOnce(Return(Status()));
+
+ auto adb_client = new MockAdbClient();
+ EXPECT_CALL(*adb_client, GetSyncService(_))
+ .Times(1)
+ .WillOnce(Return(ByMove(SyncServiceUP(sync_service))));
+
+ EXPECT_CALL(*this, GetAdbClient(_))
+ .Times(1)
+ .WillOnce(Return(ByMove(AdbClientUP(adb_client))));
+
+ EXPECT_TRUE(GetFile(FileSpec("/data/local/tmp/test"), FileSpec()).Success());
+}
+
+TEST_F(PlatformAndroidTest, GetFileWithCatFallback) {
+ auto sync_service = new MockSyncService();
+ EXPECT_CALL(
+ *sync_service,
+ Stat(FileSpec("/data/data/com.example.app/lib-main/libtest.so"), _, _, _))
+ .Times(1)
+ .WillOnce(DoAll(SetArgReferee<1>(0), Return(Status())));
+
+ auto adb_client0 = new MockAdbClient();
+ EXPECT_CALL(*adb_client0, GetSyncService(_))
+ .Times(1)
+ .WillOnce(Return(ByMove(SyncServiceUP(sync_service))));
+
+ auto adb_client1 = new MockAdbClient();
+ EXPECT_CALL(
+ *adb_client1,
+ ShellToFile(StrEq("cat '/data/data/com.example.app/lib-main/libtest.so'"),
+ _, _))
+ .Times(1)
+ .WillOnce(Return(Status()));
+
+ EXPECT_CALL(*this, GetAdbClient(_))
+ .Times(2)
+ .WillOnce(Return(ByMove(AdbClientUP(adb_client0))))
+ .WillOnce(Return(ByMove(AdbClientUP(adb_client1))));
+
+ EXPECT_TRUE(
+ GetFile(FileSpec("/data/data/com.example.app/lib-main/libtest.so"),
+ FileSpec())
+ .Success());
+}
More information about the lldb-commits
mailing list