diff --git a/tools/projmgr/include/ProjMgrWorker.h b/tools/projmgr/include/ProjMgrWorker.h index 86871c6ff..482aa927b 100644 --- a/tools/projmgr/include/ProjMgrWorker.h +++ b/tools/projmgr/include/ProjMgrWorker.h @@ -704,6 +704,12 @@ class ProjMgrWorker { */ void SetUpCommand(bool isSetup); + /** + * @brief set flag when running in rpc mode + * @param boolean rpcMode + */ + void RpcMode(bool rpcMode); + /** * @brief set yaml emitter * @param pointer to yaml emitter @@ -961,6 +967,7 @@ class ProjMgrWorker { bool m_relativePaths; bool m_cbuild2cmake; bool m_isSetupCommand; + bool m_rpcMode = false; std::set m_undefLayerVars; StrMap m_packMetadata; std::map m_executes; diff --git a/tools/projmgr/src/ProjMgr.cpp b/tools/projmgr/src/ProjMgr.cpp index d170b19c4..28c6463fc 100644 --- a/tools/projmgr/src/ProjMgr.cpp +++ b/tools/projmgr/src/ProjMgr.cpp @@ -424,6 +424,7 @@ int ProjMgr::ProcessCommands() { } else if (m_command == "rpc") { // Launch 'rpc' server over stdin/stdout ProjMgrLogger::m_silent = true; + m_worker.RpcMode(true); if (!m_rpcServer.Run()) { return ErrorCode::ERROR; } diff --git a/tools/projmgr/src/ProjMgrRpcServer.cpp b/tools/projmgr/src/ProjMgrRpcServer.cpp index ed1d9d799..0d9f81e35 100644 --- a/tools/projmgr/src/ProjMgrRpcServer.cpp +++ b/tools/projmgr/src/ProjMgrRpcServer.cpp @@ -138,9 +138,10 @@ bool ProjMgrRpcServer::Run(void) { // Send response if (m_contextLength) { - cout << CONTENT_LENGTH_HEADER << response.size() << std::endl << std::endl; + cout << CONTENT_LENGTH_HEADER << response.size() << std::endl << std::endl << response << std::flush; + } else { + cout << response << std::endl; } - cout << response << std::flush; if (m_debug) { log << response << std::endl; diff --git a/tools/projmgr/src/ProjMgrWorker.cpp b/tools/projmgr/src/ProjMgrWorker.cpp index f8c8e29fd..9ccb92e94 100644 --- a/tools/projmgr/src/ProjMgrWorker.cpp +++ b/tools/projmgr/src/ProjMgrWorker.cpp @@ -298,6 +298,10 @@ void ProjMgrWorker::SetUpCommand(bool isSetup) { m_isSetupCommand = isSetup; } +void ProjMgrWorker::RpcMode(bool rpcMode) { + m_rpcMode = rpcMode; +} + void ProjMgrWorker::SetEmitter(ProjMgrYamlEmitter* emitter) { m_emitter = emitter; } @@ -1919,6 +1923,11 @@ bool ProjMgrWorker::ProcessComponents(ContextItem& context) { return false; } + // tolerate component selection errors when in rpc mode + if (m_rpcMode) { + error = false; + } + return !error; } diff --git a/tools/projmgr/test/data/TestRpc/minimal.cproject.yml b/tools/projmgr/test/data/TestRpc/minimal.cproject.yml new file mode 100644 index 000000000..42a444d17 --- /dev/null +++ b/tools/projmgr/test/data/TestRpc/minimal.cproject.yml @@ -0,0 +1,7 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/cproject.schema.json + +project: + + components: + - component: Startup + - component: CORE diff --git a/tools/projmgr/test/data/TestRpc/minimal.csolution.yml b/tools/projmgr/test/data/TestRpc/minimal.csolution.yml new file mode 100644 index 000000000..469b7f75c --- /dev/null +++ b/tools/projmgr/test/data/TestRpc/minimal.csolution.yml @@ -0,0 +1,15 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + + compiler: AC6 + + target-types: + - type: TestHW + device: RteTest_ARMCM4_NOFP + + projects: + - project: minimal.cproject.yml + + packs: + - pack: ARM::RteTest_DFP diff --git a/tools/projmgr/test/data/TestRpc/unknown-component.cproject.yml b/tools/projmgr/test/data/TestRpc/unknown-component.cproject.yml new file mode 100644 index 000000000..5fd750e40 --- /dev/null +++ b/tools/projmgr/test/data/TestRpc/unknown-component.cproject.yml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/cproject.schema.json + +project: + + components: + - component: Startup + - component: CORE + - component: ARM::UNKNOWN:COMPONENT diff --git a/tools/projmgr/test/data/TestRpc/unknown-component.csolution.yml b/tools/projmgr/test/data/TestRpc/unknown-component.csolution.yml new file mode 100644 index 000000000..e236789ca --- /dev/null +++ b/tools/projmgr/test/data/TestRpc/unknown-component.csolution.yml @@ -0,0 +1,15 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Open-CMSIS-Pack/devtools/main/tools/projmgr/schemas/csolution.schema.json + +solution: + + compiler: AC6 + + target-types: + - type: TestHW + device: RteTest_ARMCM4_NOFP + + projects: + - project: unknown-component.cproject.yml + + packs: + - pack: ARM::RteTest_DFP diff --git a/tools/projmgr/test/src/ProjMgrRpcTests.cpp b/tools/projmgr/test/src/ProjMgrRpcTests.cpp index 7e45c02c1..eee1123ca 100644 --- a/tools/projmgr/test/src/ProjMgrRpcTests.cpp +++ b/tools/projmgr/test/src/ProjMgrRpcTests.cpp @@ -8,10 +8,9 @@ #include "ProjMgrTestEnv.h" #include "ProjMgrRpcServer.h" #include "ProjMgrRpcServerData.h" - - #include "ProjMgrLogger.h" +#include "ProductInfo.h" using namespace std; @@ -19,12 +18,66 @@ class ProjMgrRpcTests : public ProjMgr, public ::testing::Test { protected: ProjMgrRpcTests() {} virtual ~ProjMgrRpcTests() {} + string FormatRequest(const int id, const string& method, const json& params); + vector RunRpcMethods(const string& strIn); }; -TEST_F(ProjMgrRpcTests, Load_Solution) { +string ProjMgrRpcTests::FormatRequest(const int id, const string& method, const json& params = json()) { + json request; + request["jsonrpc"] = "2.0"; + request["id"] = id; + request["method"] = method; + if (!params.is_null()) { + request["params"] = params; + } + return request.dump(); +} + +vector ProjMgrRpcTests::RunRpcMethods(const string& strIn) { + StdStreamRedirect streamRedirect; + streamRedirect.SetInString(strIn); + char* argv[] = { (char*)"csolution", (char*)"rpc" }; + EXPECT_EQ(0, RunProjMgr(2, argv, 0)); + string line; + vector responses; + istringstream iss(streamRedirect.GetOutString()); + while (getline(iss, line)) { + responses.push_back(json::parse(line)); + } + return responses; +} +TEST_F(ProjMgrRpcTests, RpcGetVersion) { + const auto& requests = FormatRequest(1, "GetVersion"); + const auto& responses = RunRpcMethods(requests); + EXPECT_EQ("2.0", responses[0]["jsonrpc"]); + EXPECT_EQ(1, responses[0]["id"]); + EXPECT_EQ(string(VERSION_STRING), responses[0]["result"]); +} + +TEST_F(ProjMgrRpcTests, RpcLoadSolution) { + const string& csolution = testinput_folder + "/TestRpc/minimal.csolution.yml"; + const auto& requests = + FormatRequest(1, "LoadPacks") + + FormatRequest(2, "LoadSolution", json({{ "solution", csolution }})); + + const auto& responses = RunRpcMethods(requests); + EXPECT_TRUE(responses[0]["result"]); + EXPECT_TRUE(responses[1]["result"]); +} +TEST_F(ProjMgrRpcTests, RpcLoadSolution_UnknownComponent) { + const string& csolution = testinput_folder + "/TestRpc/unknown-component.csolution.yml"; + const auto& requests = + FormatRequest(1, "LoadPacks") + + FormatRequest(2, "LoadSolution", json({ { "solution", csolution } })) + + FormatRequest(3, "GetLogMessages"); + const auto& responses = RunRpcMethods(requests); + EXPECT_TRUE(responses[0]["result"]); + EXPECT_TRUE(responses[1]["result"]); + EXPECT_EQ("no component was found with identifier 'ARM::UNKNOWN:COMPONENT'", + responses[2]["result"]["errors"][0]); } // end of ProjMgrRpcTests.cpp diff --git a/tools/projmgr/test/src/ProjMgrTestEnv.cpp b/tools/projmgr/test/src/ProjMgrTestEnv.cpp index 59143b681..6b1a39f60 100644 --- a/tools/projmgr/test/src/ProjMgrTestEnv.cpp +++ b/tools/projmgr/test/src/ProjMgrTestEnv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 Arm Limited. All rights reserved. + * Copyright (c) 2020-2025 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -44,6 +44,11 @@ void StdStreamRedirect::ClearStringStreams() { m_cerrbuffer.str(string()); } +void StdStreamRedirect::SetInString(const std::string& inStr) { + m_inputBuffer = std::istringstream(inStr); + std::cin.rdbuf(m_inputBuffer.rdbuf()); +} + StdStreamRedirect::~StdStreamRedirect() { // reverse redirect std::cout.rdbuf(m_stdoutStreamBuf); diff --git a/tools/projmgr/test/src/ProjMgrTestEnv.h b/tools/projmgr/test/src/ProjMgrTestEnv.h index b693dfe9f..757fdf03e 100644 --- a/tools/projmgr/test/src/ProjMgrTestEnv.h +++ b/tools/projmgr/test/src/ProjMgrTestEnv.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 Arm Limited. All rights reserved. + * Copyright (c) 2020-2025 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -31,12 +31,14 @@ class StdStreamRedirect { std::string GetOutString(); std::string GetErrorString(); void ClearStringStreams(); + void SetInString(const std::string& inStr); private: std::stringstream m_outbuffer; std::stringstream m_cerrbuffer; std::streambuf* m_stdoutStreamBuf; std::streambuf* m_stdcerrStreamBuf; + std::istringstream m_inputBuffer; }; class TempSwitchCwd {