refactor: replace nlohmann_json with cJSON and refactor CLI handler

- Replace nlohmann::json with cJSON for JSON output (remove heavy dependency)
- Split CmdHelp into PrintFullHelp + PrintSubcommandHelp (reduce cyclomatic complexity 12→3/4)
- Extract HasHelpFlag helper from HandleCommand (reduce cyclomatic complexity 6→5)
- Fix bug: PrintSubcommandHelp return value now propagated (unknown-cmd --help returns 1)
- Rename global variables to g_camelCase convention (g_commands, g_programName, etc.)
- Replace magic number with CJSON_DEEP_COPY constant
- Add test cases 031-032 for PrintSubcommandHelp failure path and top-level --help priority
- Remove nlohmann_json_static from both BUILD.gn files

Co-Authored-By: Agent
Change-Id: Ie3a7c1b2d4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9
Signed-off-by: wangyantian <wangyantian@huawei.com>
This commit is contained in:
wangyantian
2026-04-30 20:08:40 +08:00
parent d39f970e37
commit e9bc5e652d
7 changed files with 278 additions and 141 deletions
+1 -2
View File
@@ -72,8 +72,7 @@
"input",
"distributed_notification_service",
"i18n",
"image_framework",
"json"
"image_framework"
]
},
"build": {
-1
View File
@@ -39,7 +39,6 @@ ohos_executable("ohos-batteryManager") {
"c_utils:utils",
"hilog:libhilog",
"ipc:ipc_core",
"json:nlohmann_json_static",
"samgr:samgr_proxy",
]
+1 -1
View File
@@ -12,7 +12,7 @@
"type": "object",
"properties": {}
},
"hasSubcommands": true,
"hasSubCommand": true,
"subcommands": {
"capacity": {
"description": "Query battery capacity percentage (0-100%). Used for battery status checking and system management scenarios. Not applicable for real-time monitoring or subscription-based battery events.",
+5
View File
@@ -40,6 +40,11 @@ ohos-batteryManager <command> [options]
# View all command help
ohos-batteryManager --help
# View subcommand help
ohos-batteryManager capacity --help
ohos-batteryManager total-energy --help
ohos-batteryManager remain-energy --help
# Query battery capacity
ohos-batteryManager capacity
# Output: {"type":"result","status":"success","data":{"capacity":85}}
+123 -123
View File
@@ -24,16 +24,10 @@
#include "battery_info.h"
#include "battery_srv_client.h"
#include "battery_srv_errors.h"
#include "nlohmann/json.hpp"
#include "cJSON.h"
namespace {
// Log macros: help output uses CLI_LOG (no prefix), errors use CLI_ERROR
#define CLI_LOG(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
#define CLI_ERROR(fmt, ...) fprintf(stderr, "[ERROR] " fmt "\n", ##__VA_ARGS__)
using CommandHandler = std::function<int(int, char**)>;
// Command struct matching spec template (03-1-code-template.md)
struct Command {
const char* name;
const char* description;
@@ -43,118 +37,144 @@ struct Command {
CommandHandler handler;
};
static std::unordered_map<std::string, Command> gCommands;
static const char* gProgramName = "";
static bool gHasSubcommands = false;
static const char* gToolDescription =
struct cJSONDeleter {
void operator()(cJSON* obj) const { cJSON_Delete(obj); }
};
using cJSONPtr = std::unique_ptr<cJSON, cJSONDeleter>;
static constexpr uint32_t CLI_SUCCESS = 0;
static constexpr uint32_t CLI_FAILURE = 1;
static constexpr uint32_t CLI_CMD_PARAM_INDEX_1 = 1;
static constexpr uint32_t CLI_CMD_PARAM_INDEX_2 = 2;
static constexpr int CJSON_DEEP_COPY = 1;
static const char* CLI_TOOL_NAME = nullptr;
static constexpr const char* TOOL_DESCRIPTION =
"Battery capacity and energy query tool. "
"Used for system management, maintenance troubleshooting and battery status inspection. "
"Not applicable for real-time battery monitoring or battery event subscription.";
static constexpr uint32_t CLI_CMD_MIN_PARAM_COUNT = 2;
static std::unordered_map<std::string, Command> g_commands;
static bool g_hasSubcommands = false;
// Registration macro matching spec template
#define CLI_LOG(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
#define CLI_ERROR(fmt, ...) fprintf(stderr, "[ERROR] " fmt "\n", ##__VA_ARGS__)
#define REGISTER_CMD(name, desc, usage, params, examples, handler) \
gCommands[name] = { name, desc, usage, params, examples, handler }
g_commands[name] = { name, desc, usage, params, examples, handler }
bool HasHelpFlag(int argc, char** argv, int start)
{
for (int i = start; i < argc; i++) {
if (strcmp(argv[i], "--help") == 0) {
return true;
}
}
return false;
}
} // namespace
// Success response: includes type field per spec
static int OutputSuccess(const nlohmann::json& data)
static int OutputSuccess(const cJSONPtr& data)
{
nlohmann::json response;
response["type"] = "result";
response["status"] = "success";
response["data"] = data;
std::cout << response.dump() << std::endl;
return 0;
cJSONPtr root(cJSON_CreateObject());
if (!root) {
CLI_ERROR("Failed to create JSON response");
return CLI_FAILURE;
}
cJSON_AddStringToObject(root.get(), "type", "result");
cJSON_AddStringToObject(root.get(), "status", "success");
cJSON_AddItemToObject(root.get(), "data", cJSON_Duplicate(data.get(), CJSON_DEEP_COPY));
char* output = cJSON_PrintUnformatted(root.get());
if (output) {
std::cout << output << std::endl;
cJSON_free(output);
}
return CLI_SUCCESS;
}
// Error response: includes type field, no data field per spec
static int OutputError(const std::string& errCode, const std::string& errMsg,
const std::string& suggestion)
{
nlohmann::json response;
response["type"] = "result";
response["status"] = "error";
response["errCode"] = errCode;
response["errMsg"] = errMsg;
response["suggestion"] = suggestion;
std::cout << response.dump() << std::endl;
return 1;
cJSONPtr root(cJSON_CreateObject());
if (!root) {
CLI_ERROR("Failed to create JSON response");
return CLI_FAILURE;
}
cJSON_AddStringToObject(root.get(), "type", "result");
cJSON_AddStringToObject(root.get(), "status", "error");
cJSON_AddStringToObject(root.get(), "errCode", errCode.c_str());
cJSON_AddStringToObject(root.get(), "errMsg", errMsg.c_str());
cJSON_AddStringToObject(root.get(), "suggestion", suggestion.c_str());
char* output = cJSON_PrintUnformatted(root.get());
if (output) {
std::cout << output << std::endl;
cJSON_free(output);
}
return CLI_FAILURE;
}
// Help command matching spec template (03-1-code-template.md)
static int CmdHelp(int argc, char** argv)
static void PrintFullHelp()
{
std::string targetCmd;
for (int i = 1; i < argc; i++) {
if (argv[i][0] != '-') {
targetCmd = argv[i];
CLI_LOG("%s - %s", CLI_TOOL_NAME, TOOL_DESCRIPTION);
CLI_LOG("");
CLI_LOG("Usage:");
CLI_LOG(" %s [options]", CLI_TOOL_NAME);
if (g_hasSubcommands) {
CLI_LOG(" %s <command> [options]", CLI_TOOL_NAME);
}
CLI_LOG("");
CLI_LOG("Parameters:");
CLI_LOG(" --help Display this help message");
if (g_hasSubcommands) {
CLI_LOG("");
CLI_LOG("SubCommands:");
for (const auto& pair : g_commands) {
CLI_LOG(" %-18s %s", pair.first.c_str(),
pair.second.description ? pair.second.description : "");
}
}
CLI_LOG("");
CLI_LOG("Examples:");
if (g_hasSubcommands) {
CLI_LOG(" %s --help", CLI_TOOL_NAME);
for (const auto& pair : g_commands) {
CLI_LOG(" %s %s --help", CLI_TOOL_NAME, pair.first.c_str());
break;
}
}
}
// ========== Full help (no subcommand argument) ==========
if (targetCmd.empty()) {
// Title: <name> - <description>
CLI_LOG("%s - %s", gProgramName, gToolDescription);
CLI_LOG("");
CLI_LOG("Usage:");
CLI_LOG(" %s [options]", gProgramName);
if (gHasSubcommands) {
CLI_LOG(" %s <command> [options]", gProgramName);
}
CLI_LOG("");
CLI_LOG("Parameters:");
CLI_LOG(" --help Display this help message");
if (gHasSubcommands) {
CLI_LOG("");
CLI_LOG("SubCommands:");
for (const auto& pair : gCommands) {
CLI_LOG(" %-18s %s", pair.first.c_str(),
pair.second.description ? pair.second.description : "");
}
}
CLI_LOG("");
CLI_LOG("Examples:");
if (gHasSubcommands) {
CLI_LOG(" %s --help", gProgramName);
for (const auto& pair : gCommands) {
CLI_LOG(" %s %s --help", gProgramName, pair.first.c_str());
break;
}
}
return 0;
}
// ========== Single command help ==========
auto it = gCommands.find(targetCmd);
if (it == gCommands.end()) {
static int PrintSubcommandHelp(const std::string& targetCmd)
{
auto it = g_commands.find(targetCmd);
if (it == g_commands.end()) {
CLI_ERROR("Unknown command: %s", targetCmd.c_str());
return 1;
return CLI_FAILURE;
}
const Command& cmd = it->second;
// Title: <name> <cmd> - <description>
CLI_LOG("%s %s - %s", gProgramName, cmd.name, cmd.description ? cmd.description : "N/A");
// Usage
CLI_LOG("%s %s - %s", CLI_TOOL_NAME, cmd.name, cmd.description ? cmd.description : "N/A");
if (cmd.usage) {
CLI_LOG("");
CLI_LOG("Usage:");
CLI_LOG(" %s", cmd.usage);
}
// Parameters
if (cmd.parameters) {
CLI_LOG("");
CLI_LOG("Parameters:");
CLI_LOG("%s", cmd.parameters);
}
CLI_LOG(" %-18s %s", "--help", "Display this help message");
// Examples
if (cmd.examples) {
CLI_LOG("");
CLI_LOG("Examples:");
CLI_LOG("%s", cmd.examples);
}
return 0;
return CLI_SUCCESS;
}
static void PrintUsage(const char* prog)
{
CLI_ERROR("Usage: %s <command> [options]", prog);
CLI_ERROR("Run '%s --help' for more information.", prog);
}
static int CmdCapacity([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
@@ -174,8 +194,8 @@ static int CmdCapacity([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
"Check if powermgr process is running: ps -ef | grep powermgr");
}
nlohmann::json data;
data["capacity"] = capacity;
cJSONPtr data(cJSON_CreateObject());
cJSON_AddNumberToObject(data.get(), "capacity", capacity);
return OutputSuccess(data);
}
@@ -196,8 +216,8 @@ static int CmdTotalEnergy([[maybe_unused]] int argc, [[maybe_unused]] char** arg
"Check if powermgr process is running and caller has system permission.");
}
nlohmann::json data;
data["totalEnergy"] = totalEnergy;
cJSONPtr data(cJSON_CreateObject());
cJSON_AddNumberToObject(data.get(), "totalEnergy", totalEnergy);
return OutputSuccess(data);
}
@@ -218,8 +238,8 @@ static int CmdRemainEnergy([[maybe_unused]] int argc, [[maybe_unused]] char** ar
"Check if powermgr process is running and caller has system permission.");
}
nlohmann::json data;
data["remainEnergy"] = remainEnergy;
cJSONPtr data(cJSON_CreateObject());
cJSON_AddNumberToObject(data.get(), "remainEnergy", remainEnergy);
return OutputSuccess(data);
}
@@ -246,52 +266,32 @@ static void InitCommands()
" ohos-batteryManager remain-energy",
CmdRemainEnergy);
gHasSubcommands = (gCommands.size() > 1);
}
static void PrintUsage(const char* prog)
{
CLI_ERROR("Usage: %s <command> [options]", prog);
CLI_ERROR("Run '%s --help' for more information.", prog);
g_hasSubcommands = (!g_commands.empty());
}
int HandleCommand(int argc, char** argv)
{
gProgramName = argv[0];
// Top-level --help check (before minimum args check)
if (argc >= 2 && strcmp(argv[1], "--help") == 0) {
InitCommands();
char* helpArgv[1] = { argv[0] };
CmdHelp(1, helpArgv);
return 0;
}
if (argc < CLI_CMD_MIN_PARAM_COUNT) {
PrintUsage(argv[0]);
return 1;
}
CLI_TOOL_NAME = argv[0];
InitCommands();
std::string cmdName = argv[1];
// Subcommand --help check (before command dispatch)
for (int i = 2; i < argc; i++) {
if (strcmp(argv[i], "--help") == 0) {
char* helpArgv[2] = { argv[0], const_cast<char*>(cmdName.c_str()) };
CmdHelp(2, helpArgv);
return 0;
}
if (argc < CLI_CMD_PARAM_INDEX_2) {
PrintUsage(argv[0]);
return CLI_FAILURE;
}
if (strcmp(argv[CLI_CMD_PARAM_INDEX_1], "--help") == 0) {
PrintFullHelp();
return CLI_SUCCESS;
}
auto it = gCommands.find(cmdName);
if (it == gCommands.end()) {
std::string cmdName = argv[CLI_CMD_PARAM_INDEX_1];
auto it = g_commands.find(cmdName);
if (it == g_commands.end()) {
CLI_ERROR("Unknown command: %s", cmdName.c_str());
PrintUsage(argv[0]);
return 1;
return CLI_FAILURE;
}
int cmdArgc = argc - CLI_CMD_MIN_PARAM_COUNT;
char** cmdArgv = argv + CLI_CMD_MIN_PARAM_COUNT;
return it->second.handler(cmdArgc, cmdArgv);
if (HasHelpFlag(argc, argv, CLI_CMD_PARAM_INDEX_2)) {
return PrintSubcommandHelp(cmdName);
}
return it->second.handler(argc - CLI_CMD_PARAM_INDEX_2, argv + CLI_CMD_PARAM_INDEX_2);
}
-1
View File
@@ -39,7 +39,6 @@ ohos_unittest("test_ohos_battery_manager_cli") {
"googletest:gtest_main",
"hilog:libhilog",
"ipc:ipc_core",
"json:nlohmann_json_static",
"samgr:samgr_proxy",
]
+148 -13
View File
@@ -14,7 +14,10 @@
*/
#include <gtest/gtest.h>
#include <cstdio>
#include <cstring>
#include <string>
#include <sstream>
#include "battery_info.h"
#include "battery_srv_client.h"
@@ -69,7 +72,7 @@ void BatteryManagerCliTest::TearDown() {}
/**
* @tc.name: BatteryManagerCliTest_001
* @tc.desc: Test no arguments returns error
* @tc.desc: Test no arguments returns error (argc < 2 path)
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_001, TestSize.Level1)
{
@@ -92,7 +95,7 @@ HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_002, TestSize.Level1)
/**
* @tc.name: BatteryManagerCliTest_003
* @tc.desc: Test "help" subcommand is not supported (treated as unknown command)
* @tc.desc: Test "help" is treated as unknown command (no help subcommand)
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_003, TestSize.Level1)
{
@@ -129,7 +132,7 @@ HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_005, TestSize.Level1)
/**
* @tc.name: BatteryManagerCliTest_006
* @tc.desc: Test capacity command fails when service returns invalid value
* @tc.desc: Test capacity command fails when service returns INVALID_BATT_INT_VALUE
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_006, TestSize.Level1)
{
@@ -167,7 +170,7 @@ HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_008, TestSize.Level1)
/**
* @tc.name: BatteryManagerCliTest_009
* @tc.desc: Test total-energy command fails when service returns invalid value
* @tc.desc: Test total-energy command fails when service returns INVALID_BATT_INT_VALUE
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_009, TestSize.Level1)
{
@@ -205,7 +208,7 @@ HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_011, TestSize.Level1)
/**
* @tc.name: BatteryManagerCliTest_012
* @tc.desc: Test remain-energy command fails when service returns invalid value
* @tc.desc: Test remain-energy command fails when service returns INVALID_BATT_INT_VALUE
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_012, TestSize.Level1)
{
@@ -244,22 +247,20 @@ HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_014, TestSize.Level1)
/**
* @tc.name: BatteryManagerCliTest_015
* @tc.desc: Test capacity with negative value should still succeed
* (API may return negative values in edge cases, CLI should not reject)
* @tc.desc: Test capacity with non-INVALID negative value still succeeds
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_015, TestSize.Level1)
{
g_capacityRet = -1;
g_capacityRet = -2;
char prog[] = "ohos-batteryManager";
char cmd[] = "capacity";
char* argv[] = { prog, cmd };
// Should succeed because -1 != INVALID_BATT_INT_VALUE
EXPECT_EQ(HandleCommand(2, argv), 0);
}
/**
* @tc.name: BatteryManagerCliTest_016
* @tc.desc: Test all commands can be called sequentially
* @tc.desc: Test all three commands can be called sequentially
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_016, TestSize.Level1)
{
@@ -318,7 +319,7 @@ HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_019, TestSize.Level1)
/**
* @tc.name: BatteryManagerCliTest_020
* @tc.desc: Test capacity --help shows subcommand help and returns success
* @tc.desc: Test capacity --help shows subcommand help
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_020, TestSize.Level1)
{
@@ -331,7 +332,7 @@ HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_020, TestSize.Level1)
/**
* @tc.name: BatteryManagerCliTest_021
* @tc.desc: Test total-energy --help shows subcommand help and returns success
* @tc.desc: Test total-energy --help shows subcommand help
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_021, TestSize.Level1)
{
@@ -344,7 +345,7 @@ HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_021, TestSize.Level1)
/**
* @tc.name: BatteryManagerCliTest_022
* @tc.desc: Test remain-energy --help shows subcommand help and returns success
* @tc.desc: Test remain-energy --help shows subcommand help
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_022, TestSize.Level1)
{
@@ -354,3 +355,137 @@ HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_022, TestSize.Level1)
char* argv[] = { prog, cmd, helpFlag };
EXPECT_EQ(HandleCommand(3, argv), 0);
}
/**
* @tc.name: BatteryManagerCliTest_023
* @tc.desc: Test --help after extra args still triggers subcommand help
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_023, TestSize.Level1)
{
char prog[] = "ohos-batteryManager";
char cmd[] = "capacity";
char arg1[] = "some_arg";
char helpFlag[] = "--help";
char* argv[] = { prog, cmd, arg1, helpFlag };
EXPECT_EQ(HandleCommand(4, argv), 0);
}
/**
* @tc.name: BatteryManagerCliTest_024
* @tc.desc: Test total-energy with non-INVALID negative value still succeeds
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_024, TestSize.Level1)
{
g_totalEnergyRet = -2;
char prog[] = "ohos-batteryManager";
char cmd[] = "total-energy";
char* argv[] = { prog, cmd };
EXPECT_EQ(HandleCommand(2, argv), 0);
}
/**
* @tc.name: BatteryManagerCliTest_025
* @tc.desc: Test remain-energy with non-INVALID negative value still succeeds
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_025, TestSize.Level1)
{
g_remainEnergyRet = -2;
char prog[] = "ohos-batteryManager";
char cmd[] = "remain-energy";
char* argv[] = { prog, cmd };
EXPECT_EQ(HandleCommand(2, argv), 0);
}
/**
* @tc.name: BatteryManagerCliTest_026
* @tc.desc: Test capacity with multiple extra arguments returns error
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_026, TestSize.Level1)
{
char prog[] = "ohos-batteryManager";
char cmd[] = "capacity";
char extra1[] = "arg1";
char extra2[] = "arg2";
char* argv[] = { prog, cmd, extra1, extra2 };
EXPECT_EQ(HandleCommand(4, argv), 1);
}
/**
* @tc.name: BatteryManagerCliTest_027
* @tc.desc: Test total-energy with multiple extra arguments returns error
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_027, TestSize.Level1)
{
char prog[] = "ohos-batteryManager";
char cmd[] = "total-energy";
char extra1[] = "arg1";
char extra2[] = "arg2";
char* argv[] = { prog, cmd, extra1, extra2 };
EXPECT_EQ(HandleCommand(4, argv), 1);
}
/**
* @tc.name: BatteryManagerCliTest_028
* @tc.desc: Test remain-energy with multiple extra arguments returns error
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_028, TestSize.Level1)
{
char prog[] = "ohos-batteryManager";
char cmd[] = "remain-energy";
char extra1[] = "arg1";
char extra2[] = "arg2";
char* argv[] = { prog, cmd, extra1, extra2 };
EXPECT_EQ(HandleCommand(4, argv), 1);
}
/**
* @tc.name: BatteryManagerCliTest_029
* @tc.desc: Test capacity with large positive value succeeds
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_029, TestSize.Level1)
{
g_capacityRet = 10000;
char prog[] = "ohos-batteryManager";
char cmd[] = "capacity";
char* argv[] = { prog, cmd };
EXPECT_EQ(HandleCommand(2, argv), 0);
}
/**
* @tc.name: BatteryManagerCliTest_030
* @tc.desc: Test repeated execution of same command succeeds
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_030, TestSize.Level1)
{
char prog[] = "ohos-batteryManager";
char cmd[] = "capacity";
char* argv[] = { prog, cmd };
EXPECT_EQ(HandleCommand(2, argv), 0);
EXPECT_EQ(HandleCommand(2, argv), 0);
}
/**
* @tc.name: BatteryManagerCliTest_031
* @tc.desc: Test unknown-cmd --help returns error (PrintSubcommandHelp find failure)
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_031, TestSize.Level1)
{
char prog[] = "ohos-batteryManager";
char cmd[] = "nonexistent";
char helpFlag[] = "--help";
char* argv[] = { prog, cmd, helpFlag };
EXPECT_EQ(HandleCommand(3, argv), 1);
}
/**
* @tc.name: BatteryManagerCliTest_032
* @tc.desc: Test help --help triggers PrintFullHelp (argv[1]=="--help" path)
*/
HWTEST_F(BatteryManagerCliTest, BatteryManagerCliTest_032, TestSize.Level1)
{
char prog[] = "ohos-batteryManager";
char helpFlag[] = "--help";
char extra[] = "--help";
char* argv[] = { prog, helpFlag, extra };
EXPECT_EQ(HandleCommand(3, argv), 0);
}