blob: cad40ee8aa5119f6a7b14d434054497258817ab9 [file]
/*
* Copyright (C) 2026 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 "src/trace_processor/shell/subcommand.h"
#include <unordered_set>
#include <vector>
#include "perfetto/base/status.h"
#include "test/gtest_and_gmock.h"
namespace perfetto::trace_processor::shell {
namespace {
// A minimal Subcommand implementation for testing.
class FakeSubcommand : public Subcommand {
public:
explicit FakeSubcommand(const char* n) : name_(n) {}
const char* name() const override { return name_; }
const char* description() const override { return ""; }
const char* usage_args() const override { return ""; }
const char* detailed_help() const override { return ""; }
std::vector<FlagSpec> GetFlags() override { return {}; }
base::Status Run(const SubcommandContext&) override {
return base::OkStatus();
}
private:
const char* name_;
};
// Helper to build an argv array from an initializer list.
struct ArgvHolder {
std::vector<std::string> strings;
std::vector<char*> ptrs;
static ArgvHolder Make(std::initializer_list<const char*> args) {
ArgvHolder h;
for (const char* a : args) {
h.strings.emplace_back(a);
}
for (auto& s : h.strings) {
h.ptrs.push_back(s.data());
}
return h;
}
int argc() const { return static_cast<int>(ptrs.size()); }
char** argv() { return ptrs.data(); }
};
TEST(FindSubcommandTest, EmptyArgvReturnsNull) {
FakeSubcommand query("query");
std::vector<Subcommand*> subs = {&query};
auto args = ArgvHolder::Make({"tp_shell"});
auto result = FindSubcommandInArgs(args.argc(), args.argv(), subs, {});
EXPECT_EQ(result.subcommand, nullptr);
}
TEST(FindSubcommandTest, OnlyFlagsReturnsNull) {
FakeSubcommand query("query");
std::vector<Subcommand*> subs = {&query};
auto args = ArgvHolder::Make({"tp_shell", "-v", "--full-sort"});
auto result = FindSubcommandInArgs(args.argc(), args.argv(), subs, {});
EXPECT_EQ(result.subcommand, nullptr);
}
TEST(FindSubcommandTest, UnknownPositionalReturnsNull) {
FakeSubcommand query("query");
std::vector<Subcommand*> subs = {&query};
auto args = ArgvHolder::Make({"tp_shell", "trace.pb"});
auto result = FindSubcommandInArgs(args.argc(), args.argv(), subs, {});
EXPECT_EQ(result.subcommand, nullptr);
}
TEST(FindSubcommandTest, KnownSubcommandReturnsPtr) {
FakeSubcommand query("query");
FakeSubcommand serve("serve");
std::vector<Subcommand*> subs = {&query, &serve};
auto args = ArgvHolder::Make({"tp_shell", "query", "-c", "SELECT 1"});
auto result = FindSubcommandInArgs(args.argc(), args.argv(), subs, {});
EXPECT_EQ(result.subcommand, &query);
EXPECT_EQ(result.argv_index, 1);
}
TEST(FindSubcommandTest, FlagWithArgSkipsValue) {
FakeSubcommand query("query");
std::vector<Subcommand*> subs = {&query};
// --dev-flag takes an argument "x=y", so "query" at index 3 should be found.
auto args =
ArgvHolder::Make({"tp_shell", "--dev-flag", "x=y", "query", "trace.pb"});
std::unordered_set<std::string> fwa = {"--dev-flag"};
auto result = FindSubcommandInArgs(args.argc(), args.argv(), subs, fwa);
EXPECT_EQ(result.subcommand, &query);
EXPECT_EQ(result.argv_index, 3);
}
TEST(FindSubcommandTest, SubcommandAfterFlags) {
FakeSubcommand query("query");
std::vector<Subcommand*> subs = {&query};
auto args =
ArgvHolder::Make({"tp_shell", "--dev", "query", "-c", "sql", "trace.pb"});
auto result = FindSubcommandInArgs(args.argc(), args.argv(), subs, {});
EXPECT_EQ(result.subcommand, &query);
EXPECT_EQ(result.argv_index, 2);
}
} // namespace
} // namespace perfetto::trace_processor::shell