diff --git a/libs/rtemodel/include/RteKernel.h b/libs/rtemodel/include/RteKernel.h index 0f6a4e375..b895a0232 100644 --- a/libs/rtemodel/include/RteKernel.h +++ b/libs/rtemodel/include/RteKernel.h @@ -320,6 +320,13 @@ class RteKernel */ void SetToolInfo(const XmlItem& attr) { m_toolInfo = attr; } + /** + * @brief read the latest available pack version and resolved PDSC file for each pack. + * @param Output map of pack ID to latest version and resolved PDSC file path + * @return true if at least one latest pack entry was found, otherwise false + */ + bool ReadPackLatestVerAndPath(std::map>& latestPacks) const; + protected: /** * @brief get local pdsc files, optionally filtered @@ -330,9 +337,14 @@ class RteKernel bool GetLocalPdscFiles(const XmlItem& attr, std::map& pdscMap) const; /** * @brief parses $CMSIS_PACK_ROOT/.Local/loacl_repository.pidx file - * @return pointer to "index" element if successful, nullptr otherwise + * @return pointer to "pindex" element if successful, nullptr otherwise */ XMLTreeElement* ParseLocalRepositoryIdx() const; + /** + * @brief parses $CMSIS_PACK_ROOT/.Web/index.pidx file + * @return pointer to "pindex" element if successful, nullptr otherwise + */ + XMLTreeElement* ParseWebRepositoryIdx() const; /** * @brief create an XMLTree object to parse XML files @@ -354,6 +366,12 @@ class RteKernel */ RteKernel* GetThisKernel() const { return const_cast(this); } + /** + * @brief parses a repository index file + * @param absolute path to the repository index file + * @return pointer to "pindex" element if successful, nullptr otherwise + */ + XMLTreeElement* ParseRepositoryIdx(const std::string& indexPath) const; // data protected: diff --git a/libs/rtemodel/src/RteKernel.cpp b/libs/rtemodel/src/RteKernel.cpp index 03bfcfa0f..db8dcaffb 100644 --- a/libs/rtemodel/src/RteKernel.cpp +++ b/libs/rtemodel/src/RteKernel.cpp @@ -610,9 +610,17 @@ bool RteKernel::GetLocalPdscFiles(const XmlItem& attr, std::map>& latestPacks) const { + // read latest public pack versions from /.Web/index.pidx and the corresponding latest Web PDSC file paths + unique_ptr webPIndexChild(ParseWebRepositoryIdx()); + if (webPIndexChild) { + for (auto& child : webPIndexChild->GetChildren()) { + if (!child || child->GetTag() != "pdsc") { + continue; + } + const string& vendor = child->GetAttribute("vendor"); + const string& name = child->GetAttribute("name"); + const string& version = child->GetAttribute("version"); + if (vendor.empty() || name.empty() || version.empty()) { + continue; + } + const string packId = vendor + "::" + name; + const string pdscFile = GetCmsisPackRoot() + "/.Web/" + vendor + "." + name + ".pdsc"; + latestPacks[packId] = { version, RteFsUtils::Exists(pdscFile) ? pdscFile : string() }; + } + } + // read effective PDSC files (installed + local). Override the current entry if the local version is newer than the web version + XmlItem attributes; + map effectivePdscMap; + if (GetEffectivePdscFilesAsMap(effectivePdscMap, true)) { + for (const auto& [_, localPdscFile] : effectivePdscMap) { + RtePackage* pack = LoadPack(localPdscFile); + if (!pack) { + continue; + } + const string& vendor = pack->GetVendorString(); + const string& name = pack->GetName(); + const string& version = pack->GetVersionString(); + if (vendor.empty() || name.empty() || version.empty()) { + continue; + } + const string packId = vendor + "::" + name; + auto it = latestPacks.find(packId); + if (it == latestPacks.end() || VersionCmp::Compare(it->second.first, version) < 0) { + latestPacks[packId] = { version, localPdscFile }; + } + } + } + return !latestPacks.empty(); +} unique_ptr RteKernel::CreateUniqueXmlTree(IXmlItemBuilder* itemBuilder, const std::string& ext) const { diff --git a/libs/rtemodel/test/src/RteChkTest.cpp b/libs/rtemodel/test/src/RteChkTest.cpp index 886bd60a1..c6fe370af 100644 --- a/libs/rtemodel/test/src/RteChkTest.cpp +++ b/libs/rtemodel/test/src/RteChkTest.cpp @@ -15,7 +15,7 @@ using namespace std; TEST(RteChkTest, Summary) { -const string summary = "Collecting pdsc files 8 files found\n\ +const string summary = "Collecting pdsc files 9 files found\n\ Parsing XML passed\n\ \n\ Constructing Model passed\n\ @@ -25,10 +25,10 @@ Cleaning XML data\n\ Validating Model passed\n\ \n\ Summary:\n\ -Packs: 8\n\ +Packs: 9\n\ Generic: 4\n\ DFP: 3\n\ -BSP: 1\n\ +BSP: 2\n\ \n\ Components: 61\n\ From generic packs: 36\n\ @@ -50,7 +50,7 @@ completed\n"; rteChk.AddFileDir(RteModelTestConfig::CMSIS_PACK_ROOT); int res = rteChk.RunCheckRte(); EXPECT_EQ(res, 0); - EXPECT_EQ(rteChk.GetPackCount(), 8); + EXPECT_EQ(rteChk.GetPackCount(), 9); EXPECT_EQ(rteChk.GetComponentCount(), 61); EXPECT_EQ(rteChk.GetDeviceCount(), 10); EXPECT_EQ(rteChk.GetBoardCount(), 15); diff --git a/libs/rtemodel/test/src/RteModelTest.cpp b/libs/rtemodel/test/src/RteModelTest.cpp index db9c2f34f..bfefcc745 100644 --- a/libs/rtemodel/test/src/RteModelTest.cpp +++ b/libs/rtemodel/test/src/RteModelTest.cpp @@ -143,7 +143,7 @@ TEST(RteModelTest, LoadPacks) { list files; rteKernel.GetEffectivePdscFiles(files, false); - EXPECT_EQ(files.size(), 11); + EXPECT_EQ(files.size(), 12); RteModel* rteModel = rteKernel.GetGlobalModel(); ASSERT_NE(rteModel, nullptr); diff --git a/test/packs/ARM/RteTestBoard/0.0.1/ARM.RteTestBoard.pdsc b/test/packs/ARM/RteTestBoard/0.0.1/ARM.RteTestBoard.pdsc new file mode 100644 index 000000000..dc3521d51 --- /dev/null +++ b/test/packs/ARM/RteTestBoard/0.0.1/ARM.RteTestBoard.pdsc @@ -0,0 +1,51 @@ + + + + RteTestBoard + Testing packages listing + ARM + + + + Pre-initial version 0.0.1 for testing CheckPackVerCmd + + + + + + + + + + + + uVision Simulator + + + + + + + + + + + No device board + + + + + + uVision Simulator + + + + + + + + + + + + diff --git a/test/packs/ARM/RteTestBoard/0.0.1/Board/Flash/BoardAlgo1.FLM b/test/packs/ARM/RteTestBoard/0.0.1/Board/Flash/BoardAlgo1.FLM new file mode 100644 index 000000000..49ff6d807 --- /dev/null +++ b/test/packs/ARM/RteTestBoard/0.0.1/Board/Flash/BoardAlgo1.FLM @@ -0,0 +1 @@ +CortexM4Device.FLM : not real flash algorithm diff --git a/test/packs/ARM/RteTestBoard/0.0.1/Board/Flash/BoardAlgo2.FLM b/test/packs/ARM/RteTestBoard/0.0.1/Board/Flash/BoardAlgo2.FLM new file mode 100644 index 000000000..d3de2a7a2 --- /dev/null +++ b/test/packs/ARM/RteTestBoard/0.0.1/Board/Flash/BoardAlgo2.FLM @@ -0,0 +1 @@ +CortexM4SubFamily.FLM : not real flash algorithm diff --git a/test/packs/ARM/RteTestBoard/0.0.1/Documents/README.md b/test/packs/ARM/RteTestBoard/0.0.1/Documents/README.md new file mode 100644 index 000000000..783244691 --- /dev/null +++ b/test/packs/ARM/RteTestBoard/0.0.1/Documents/README.md @@ -0,0 +1 @@ +# Board documentation diff --git a/test/packs/ARM/RteTestBoard/0.1.0/ARM.RteTestBoard.pdsc b/test/packs/ARM/RteTestBoard/0.1.0/ARM.RteTestBoard.pdsc index 29c585c11..4eaec244e 100644 --- a/test/packs/ARM/RteTestBoard/0.1.0/ARM.RteTestBoard.pdsc +++ b/test/packs/ARM/RteTestBoard/0.1.0/ARM.RteTestBoard.pdsc @@ -9,6 +9,12 @@ Initial version + + Pre-initial version 0.0.2 for testing CheckPackVerCmd + + + Pre-initial version 0.0.1 for testing CheckPackVerCmd + diff --git a/tools/projmgr/include/ProjMgr.h b/tools/projmgr/include/ProjMgr.h index c86485cc8..439d690b4 100644 --- a/tools/projmgr/include/ProjMgr.h +++ b/tools/projmgr/include/ProjMgr.h @@ -222,6 +222,7 @@ class ProjMgr { std::vector m_allContexts; std::set m_failedContext; + bool RunCheckPackVerCmd(); bool RunConfigure(); bool RunCodeGenerator(); bool RunListPacks(); diff --git a/tools/projmgr/include/ProjMgrWorker.h b/tools/projmgr/include/ProjMgrWorker.h index 76f81d907..683ba1d9a 100644 --- a/tools/projmgr/include/ProjMgrWorker.h +++ b/tools/projmgr/include/ProjMgrWorker.h @@ -1055,6 +1055,24 @@ class ProjMgrWorker { */ bool CheckMissingFiles(); + /** + * @brief check selected packs against latest versions + * @param vector of output lines describing pack update status + * @param optional filter + * @return true if check completed successfully + */ + bool CheckPackVerAndCollectRelNotes(std::vector& results, const std::string& filter = RteUtils::EMPTY_STRING); + + /** + * @brief read release notes for versions newer than the current version and up to the latest version + * @param resolved path to the latest PDSC file + * @param version currently used by the project + * @param latest available version resolved for the latest pack or the .pidx file + * @param output list of collected release note strings for versions to be filled + * @return true if at least one matching release note was found, otherwise false + */ + bool ReadPackReleaseNotes(const std::string& pdscFile, const std::string& currentVersion, const std::string& latestVersion, std::vector& releaseNotes); + /** * @brief collect unused packs for each selected context */ diff --git a/tools/projmgr/src/ProjMgr.cpp b/tools/projmgr/src/ProjMgr.cpp index 1ca551578..5e0d81c03 100644 --- a/tools/projmgr/src/ProjMgr.cpp +++ b/tools/projmgr/src/ProjMgr.cpp @@ -22,6 +22,7 @@ static constexpr const char* USAGE = "\n\ Usage:\n\ csolution [.csolution.yml] [options]\n\n\ Commands:\n\ + check Check existing project for potential pack updates\n\ convert Convert user input *.yml files to *.cbuild.yml files\n\ list boards Print list of available board names\n\ list configs Print list of configuration files\n\ @@ -179,6 +180,7 @@ int ProjMgr::ParseCommandLine(int argc, char** argv) { {"update-rte", { false, {context, contextSet, activeTargetSet, debug, load, quiet, schemaCheck, toolchain, verbose, frozenPacks}}}, {"convert", { false, {context, contextSet, activeTargetSet, debug, exportSuffix, load, quiet, schemaCheck, noUpdateRte, output, outputAlt, toolchain, verbose, frozenPacks, cbuildgen}}}, {"run", { false, {context, contextSet, activeTargetSet, debug, generator, load, quiet, schemaCheck, verbose, dryRun}}}, + {"check", { false, {context, contextSet, activeTargetSet, debug, load, quiet, schemaCheck, verbose}}}, {"list packs", { true, {context, contextSet, activeTargetSet, debug, filter, load, missing, locked, quiet, schemaCheck, toolchain, verbose}}}, {"list boards", { true, {context, contextSet, activeTargetSet, debug, filter, load, quiet, schemaCheck, toolchain, verbose}}}, {"list devices", { true, {context, contextSet, activeTargetSet, debug, filter, load, quiet, schemaCheck, toolchain, verbose}}}, @@ -397,6 +399,10 @@ int ProjMgr::ProcessCommands() { if (!m_rpcServer.Run()) { return ErrorCode::ERROR; } + } else if (m_command == "check") { + if (!RunCheckPackVerCmd()) { + return ErrorCode::ERROR; + } } else { ProjMgrLogger::Get().Error(" was not found"); return ErrorCode::ERROR; @@ -1034,6 +1040,29 @@ bool ProjMgr::RunCodeGenerator(void) { return true; } +bool ProjMgr::RunCheckPackVerCmd(void) { + if (m_csolutionFile.empty()) { + ProjMgrLogger::Get().Error("input csolution.yml was not specified"); + return false; + } + // Check input options + if (!PopulateContexts()) { + return false; + } + // Parse all input files and create contexts + if (!ParseAndValidateContexts()) { + return false; + } + + vector outputs; + const bool result = m_worker.CheckPackVerAndCollectRelNotes(outputs, m_filter); + + for (const auto& line : outputs) { + ProjMgrLogger::out() << line << std::endl; + } + return result; +} + bool ProjMgr::RunListToolchains(void) { if (!m_csolutionFile.empty()) { // Parse all input files and create contexts diff --git a/tools/projmgr/src/ProjMgrWorker.cpp b/tools/projmgr/src/ProjMgrWorker.cpp index f9bdfd1d0..58afa2c96 100644 --- a/tools/projmgr/src/ProjMgrWorker.cpp +++ b/tools/projmgr/src/ProjMgrWorker.cpp @@ -5979,6 +5979,116 @@ void ProjMgrWorker::CheckMissingLinkerScript(ContextItem& context) { } } +bool ProjMgrWorker::CheckPackVerAndCollectRelNotes(std::vector& results, const std::string& filter) { + map> usedPacks; // Vendor::Name -> (currently used version, project-specified path if any) + map> latestPacks; // Vendor::Name -> (latest available version, resolved PDSC file path) + vector selectedContexts = m_selectedContexts; + if (selectedContexts.empty()) { + for (const auto& [contextName, _] : m_contexts) { + selectedContexts.push_back(contextName); + } + } + if (!m_kernel->ReadPackLatestVerAndPath(latestPacks)) { + ProjMgrLogger::Get().Error("failed to read latest pack information from pack index"); + return false; + } + for (const auto& contextName : selectedContexts) { + ContextItem& context = m_contexts.at(contextName); + if (!LoadPacks(context)) { + return false; + } + for (const auto& pack : m_loadedPacks) { + const string packId = pack->GetVendorString() + "::" + pack->GetName(); + const string& version = pack->GetVersionString(); + usedPacks[packId].first = version; + if (pack->GetPackageState() == PS_EXPLICIT_PATH) { + usedPacks[packId].second = RteFsUtils::RelativePath(pack->GetAbsolutePackagePath(), context.csolution->directory); + auto latestPack = latestPacks.find(packId); + if (latestPack == latestPacks.end() || VersionCmp::Compare(latestPack->second.first, version) < 0) { + latestPacks[packId] = { version, pack->GetPackageFileName() }; + } + } + } + } + + vector checkPackResults; + for (const auto& [packId, packInfo] : usedPacks) { + const string& currentVersion = packInfo.first; + auto latestPack = latestPacks.find(packId); + if (latestPack == latestPacks.end()) { + checkPackResults.push_back(packId + "@" + currentVersion + " (not found in CMSIS pack root or project-specified pack paths)"); + continue; + } + const string& latestVersion = latestPack->second.first; + const string& latestPdscFile = latestPack->second.second; + const bool isOutdated = VersionCmp::Compare(currentVersion, latestVersion) < 0; + string outStr = isOutdated ? + packId + "@" + currentVersion + " -> " + latestVersion : + packId + "@" + currentVersion + " (up-to-date)"; + if (m_verbose) { + const string& specifiedPackPath = packInfo.second; + if (!specifiedPackPath.empty()) { + outStr += "\n Local path: " + specifiedPackPath; + } + if (isOutdated) { + vector releaseNotes; + if (ReadPackReleaseNotes(latestPdscFile, currentVersion, latestVersion, releaseNotes)) { + for (const auto& note : releaseNotes) { + outStr += note; + } + } else { + outStr += "\n Release notes: unavailable"; + } + } + } + checkPackResults.push_back(outStr); + } + if (checkPackResults.empty()) { + ProjMgrLogger::Get().Error("no packs were found for version check"); + return false; + } + if (!filter.empty()) { + std::vector matchedPacks; + RteUtils::ApplyFilter(checkPackResults, RteUtils::SplitStringToSet(filter), matchedPacks); + if (matchedPacks.empty()) { + ProjMgrLogger::Get().Error("no packs were found for version check with filter '" + filter + "'"); + return false; + } + checkPackResults = matchedPacks; + } + results.assign(checkPackResults.begin(), checkPackResults.end()); + return true; +} + +bool ProjMgrWorker::ReadPackReleaseNotes(const string& pdscFile, const string& currentVersion, const string& latestVersion, vector& releaseNotes) { + RtePackage* pack = m_kernel->LoadPack(pdscFile); + if (!pack) { return false; } + + RteItem* releases = pack->GetReleases(); + if (!releases) { return false; } + + for (auto& child : releases->GetChildren()) { + if (!child || child->GetTag() != "release") { + continue; + } + const string& releaseVersion = child->GetAttribute("version"); + const string& text = child->GetText(); + if (releaseVersion.empty() || text.empty()) { + continue; + } + // ignore releases newer than the resolved latest version + if (VersionCmp::Compare(releaseVersion, latestVersion) > 0) { + continue; + } + // stop once the current version or an older version is reached + if (VersionCmp::Compare(releaseVersion, currentVersion) <= 0) { + break; + } + releaseNotes.push_back("\n Release notes for v" + releaseVersion + ":\n " + text); + } + return !releaseNotes.empty(); +} + void ProjMgrWorker::CollectUnusedPacks() { for (const auto& contextName : m_selectedContexts) { auto& context = m_contexts[contextName]; diff --git a/tools/projmgr/test/data/TestSolution/CheckPackVerCmd/checkPackVerCmd.cproject.yml b/tools/projmgr/test/data/TestSolution/CheckPackVerCmd/checkPackVerCmd.cproject.yml new file mode 100644 index 000000000..bf0ddcae8 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/CheckPackVerCmd/checkPackVerCmd.cproject.yml @@ -0,0 +1,4 @@ +project: + components: + - component: Startup + - component: RteTest:CORE diff --git a/tools/projmgr/test/data/TestSolution/CheckPackVerCmd/checkPackVerCmd.csolution.yml b/tools/projmgr/test/data/TestSolution/CheckPackVerCmd/checkPackVerCmd.csolution.yml new file mode 100644 index 000000000..8aa9ad0c7 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/CheckPackVerCmd/checkPackVerCmd.csolution.yml @@ -0,0 +1,16 @@ +solution: + target-types: + - type: CM0 + device: RteTest_ARMCM0 + + build-types: + - type: Debug + compiler: AC6 + + packs: + - pack: ARM::RteTest + - pack: ARM::RteTest_DFP@0.1.1 + - pack: ARM::RteTestBoard@0.0.1 + + projects: + - project: ./checkPackVerCmd.cproject.yml diff --git a/tools/projmgr/test/data/TestSolution/CheckPackVerCmd/checkPackVerCmd_my-packs.csolution.yml b/tools/projmgr/test/data/TestSolution/CheckPackVerCmd/checkPackVerCmd_my-packs.csolution.yml new file mode 100644 index 000000000..f4b1ad885 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/CheckPackVerCmd/checkPackVerCmd_my-packs.csolution.yml @@ -0,0 +1,17 @@ +solution: + target-types: + - type: CM0 + device: RteTest_ARMCM0 + + build-types: + - type: Debug + compiler: AC6 + + packs: + - pack: ARM::RteTest + - pack: ARM::RteTest_DFP@0.1.1 + - pack: ARM::RteTestBoard + path: ./my-packs/RteTestPack + + projects: + - project: ./checkPackVerCmd.cproject.yml diff --git a/tools/projmgr/test/data/TestSolution/CheckPackVerCmd/my-packs/RteTestPack/ARM.RteTestBoard.pdsc b/tools/projmgr/test/data/TestSolution/CheckPackVerCmd/my-packs/RteTestPack/ARM.RteTestBoard.pdsc new file mode 100644 index 000000000..d55757041 --- /dev/null +++ b/tools/projmgr/test/data/TestSolution/CheckPackVerCmd/my-packs/RteTestPack/ARM.RteTestBoard.pdsc @@ -0,0 +1,29 @@ + + + + RteTestBoard + Testing packages listing + ARM + + + + Active development... (a project-specified pack for testing CheckPackVerCmd) + + + Initial version + + + Pre-initial version 0.0.2 for testing CheckPackVerCmd + + + Pre-initial version 0.0.1 for testing CheckPackVerCmd + + + + + + + + + + diff --git a/tools/projmgr/test/src/ProjMgrUnitTests.cpp b/tools/projmgr/test/src/ProjMgrUnitTests.cpp index 370e40cda..d45b2f4ed 100644 --- a/tools/projmgr/test/src/ProjMgrUnitTests.cpp +++ b/tools/projmgr/test/src/ProjMgrUnitTests.cpp @@ -657,6 +657,66 @@ TEST_F(ProjMgrUnitTests, RunProjMgr_ListDependencies) { EXPECT_NE(errStr.find(expectedErr), string::npos); } +TEST_F(ProjMgrUnitTests, RunProjMgr_CheckPackVerCmd) { + char* argv[6]; + StdStreamRedirect streamRedirect; + string csolutionFile = testinput_folder + "/TestSolution/CheckPackVerCmd/checkPackVerCmd.csolution.yml"; + // no csolution file provided + argv[1] = (char*)"check"; + EXPECT_EQ(1, RunProjMgr(2, argv, 0)); + auto errStr = streamRedirect.GetErrorString(); + EXPECT_STREQ(errStr.c_str(), "error csolution: input csolution.yml was not specified\n"); + + // standard check output + argv[2] = (char*)csolutionFile.c_str(); + EXPECT_EQ(0, RunProjMgr(3, argv, 0)); + auto outStr = streamRedirect.GetOutString(); + EXPECT_STREQ(outStr.c_str(), "\ +ARM::RteTest@0.1.0 (up-to-date)\n\ +ARM::RteTestBoard@0.0.1 -> 0.1.0\n\ +ARM::RteTest_DFP@0.1.1+metadata -> 0.2.0\n"); + + // check verbose output + streamRedirect.ClearStringStreams(); + argv[3] = (char*)"--verbose"; + EXPECT_EQ(0, RunProjMgr(4, argv, 0)); + outStr = streamRedirect.GetOutString(); + EXPECT_STREQ(outStr.c_str(), "\ +ARM::RteTest@0.1.0 (up-to-date)\n\ +ARM::RteTestBoard@0.0.1 -> 0.1.0\n\ + Release notes for v0.1.0:\n\ + Initial version\n\ + Release notes for v0.0.2:\n\ + Pre-initial version 0.0.2 for testing CheckPackVerCmd\n\ +ARM::RteTest_DFP@0.1.1+metadata -> 0.2.0\n\ + Release notes for v0.2.0:\n\ + Added a new device 'RteTest_ARMCM0_Dual'\n"); + + // check verbose output with filtering + streamRedirect.ClearStringStreams(); + argv[4] = (char*)"-f"; + argv[5] = (char*)"RteTestBoard"; + EXPECT_EQ(0, RunProjMgr(6, argv, 0)); + outStr = streamRedirect.GetOutString(); + EXPECT_STREQ(outStr.c_str(), "\ +ARM::RteTestBoard@0.0.1 -> 0.1.0\n\ + Release notes for v0.1.0:\n\ + Initial version\n\ + Release notes for v0.0.2:\n\ + Pre-initial version 0.0.2 for testing CheckPackVerCmd\n"); + + // check verbose output for a project-specified pack + streamRedirect.ClearStringStreams(); + csolutionFile = testinput_folder + "/TestSolution/CheckPackVerCmd/checkPackVerCmd_my-packs.csolution.yml"; + argv[2] = (char*)csolutionFile.c_str(); + EXPECT_EQ(0, RunProjMgr(6, argv, 0)); + outStr = streamRedirect.GetOutString(); + const string expectedStr = "\ +ARM::RteTestBoard@0.2.0-dev (up-to-date)\n\ + Local path: my-packs/RteTestPack\n"; + EXPECT_STREQ(outStr.c_str(), expectedStr.c_str()); +} + TEST_F(ProjMgrUnitTests, RunProjMgr_ConvertProject_1) { char* argv[7]; string csolutionFile = UpdateTestSolutionFile("./TestProject4/test.cproject.yml");