Skip to content

Commit 508b6fd

Browse files
committed
Refactor to testable container; add tests
1 parent 5c1475d commit 508b6fd

File tree

7 files changed

+284
-64
lines changed

7 files changed

+284
-64
lines changed

src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp

Lines changed: 14 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <AppInstallerStrings.h>
1818
#include <winget/ExperimentalFeature.h>
1919
#include <winget/SelfManagement.h>
20+
#include <winget/PathTree.h>
2021
#include <winrt/Microsoft.Management.Configuration.h>
2122

2223
using namespace AppInstaller::CLI::Execution;
@@ -1695,49 +1696,14 @@ namespace AppInstaller::CLI::Workflow
16951696

16961697
// Units whose location is at this node.
16971698
std::vector<IConfigurationUnitProcessorDetails> Units;
1698-
1699-
// The children of this node.
1700-
std::map<std::filesystem::path, Node> Children;
17011699
};
17021700

1703-
Node m_rootNode;
1704-
1705-
Node* FindNode(const std::filesystem::path& path, bool createIfNeeded)
1706-
{
1707-
const auto& nodePath = std::filesystem::weakly_canonical(path);
1708-
Node* currentNode = &m_rootNode;
1709-
1710-
for (const auto& pathPart : nodePath)
1711-
{
1712-
auto& children = currentNode->Children;
1713-
1714-
if (createIfNeeded)
1715-
{
1716-
currentNode = &children[pathPart];
1717-
}
1718-
else
1719-
{
1720-
auto itr = children.find(pathPart);
1721-
1722-
if (itr != children.end())
1723-
{
1724-
currentNode = &itr->second;
1725-
}
1726-
else
1727-
{
1728-
// Not found and should not create
1729-
return nullptr;
1730-
}
1731-
}
1732-
}
1733-
1734-
return currentNode;
1735-
}
1701+
Filesystem::PathTree<Node> m_pathTree;
17361702

17371703
Node& FindNodeForFilePath(const winrt::hstring& filePath)
17381704
{
17391705
std::filesystem::path path{ std::wstring{ filePath } };
1740-
return *FindNode(path.parent_path(), true);
1706+
return m_pathTree.FindOrInsert(path.parent_path());
17411707
}
17421708

17431709
public:
@@ -1756,42 +1722,30 @@ namespace AppInstaller::CLI::Workflow
17561722

17571723
void PlacePackage(const PackageCollection::Source& source, const PackageCollection::Package& package)
17581724
{
1759-
Node* node = FindNode(package.InstalledLocation, false);
1725+
Node* node = m_pathTree.Find(package.InstalledLocation);
17601726
if (node)
17611727
{
17621728
node->Packages.emplace_back(SourceAndPackage{ source, package });
17631729
}
17641730
}
17651731

1766-
std::vector<IConfigurationUnitProcessorDetails> GetResourcesForPackage(const PackageCollection::Package& package)
1732+
std::vector<IConfigurationUnitProcessorDetails> GetResourcesForPackage(const PackageCollection::Package& package) const
17671733
{
17681734
std::vector<IConfigurationUnitProcessorDetails> result;
17691735

1770-
Node* node = FindNode(package.InstalledLocation, false);
1771-
if (node)
1772-
{
1773-
std::queue<const Node*> nodes;
1774-
nodes.push(node);
1775-
1776-
while (!nodes.empty())
1736+
m_pathTree.VisitIf(
1737+
package.InstalledLocation,
1738+
[&](const Node& node)
17771739
{
1778-
const Node* currentNode = nodes.front();
1779-
nodes.pop();
1780-
1781-
for (const auto& unit : currentNode->Units)
1740+
for (const auto& unit : node.Units)
17821741
{
17831742
result.emplace_back(unit);
17841743
}
1785-
1786-
for (const auto& child : currentNode->Children)
1787-
{
1788-
if (child.second.Packages.empty())
1789-
{
1790-
nodes.push(&child.second);
1791-
}
1792-
}
1793-
}
1794-
}
1744+
},
1745+
[](const Node& node)
1746+
{
1747+
return node.Packages.empty();
1748+
});
17951749

17961750
return result;
17971751
}

src/AppInstallerCLIE2ETests/ConfigureExportCommand.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ public void BaseSetup()
3131
var installDir = TestCommon.GetRandomTestDir();
3232
TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestPackageExport -v 1.0.0.0 --silent -l {installDir}");
3333
this.previousPathValue = System.Environment.GetEnvironmentVariable("PATH");
34-
System.Environment.SetEnvironmentVariable("PATH", this.previousPathValue + ";" + installDir);
34+
35+
// The installer puts DSCv3 resources in both locations
36+
System.Environment.SetEnvironmentVariable("PATH", this.previousPathValue + ";" + installDir + ";" + installDir + ".SubDirectory");
3537
DSCv3ResourceTestBase.EnsureTestResourcePresence();
3638
}
3739

@@ -175,6 +177,10 @@ public void ExportAll()
175177
Assert.True(showResult.StdOut.Contains("AppInstallerTest/TestResource"));
176178
Assert.True(showResult.StdOut.Contains($"Dependencies: {Constants.TestSourceName}_AppInstallerTest.TestPackageExport"));
177179
Assert.True(showResult.StdOut.Contains("data: TestData"));
180+
181+
Assert.True(showResult.StdOut.Contains("AppInstallerTest/TestResource.SubDirectory"));
182+
Assert.True(showResult.StdOut.Contains($"Dependencies: {Constants.TestSourceName}_AppInstallerTest.TestPackageExport"));
183+
Assert.True(showResult.StdOut.Contains("data: TestData"));
178184
}
179185

180186
/// <summary>

src/AppInstallerCLITests/Filesystem.cpp

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "pch.h"
44
#include "TestCommon.h"
55
#include <winget/Filesystem.h>
6+
#include <winget/PathTree.h>
67
#include <AppInstallerStrings.h>
78

89
using namespace AppInstaller::Utility;
@@ -113,3 +114,112 @@ TEST_CASE("GetExecutablePathForProcess", "[filesystem]")
113114
REQUIRE(thisExecutable.has_extension());
114115
REQUIRE(thisExecutable.filename() == L"AppInstallerCLITests.exe");
115116
}
117+
118+
TEST_CASE("PathTree_InsertAndFind", "[filesystem][pathtree]")
119+
{
120+
PathTree<bool> pathTree;
121+
122+
std::filesystem::path path1 = L"C:\\test";
123+
std::filesystem::path path1sub = L"C:\\test\\sub";
124+
std::filesystem::path path2 = L"C:\\diff";
125+
std::filesystem::path path3 = L"D:\\test";
126+
127+
REQUIRE(nullptr == pathTree.Find(path1));
128+
pathTree.FindOrInsert(path1) = true;
129+
130+
REQUIRE(nullptr != pathTree.Find(path1));
131+
REQUIRE(*pathTree.Find(path1));
132+
133+
REQUIRE(nullptr == pathTree.Find(path1sub));
134+
REQUIRE(nullptr == pathTree.Find(path2));
135+
REQUIRE(nullptr == pathTree.Find(path3));
136+
}
137+
138+
TEST_CASE("PathTree_InsertAndFind_Negative", "[filesystem][pathtree]")
139+
{
140+
PathTree<bool> pathTree;
141+
pathTree.FindOrInsert(L"C:\\a\\aa\\aaa");
142+
143+
REQUIRE(nullptr == pathTree.Find({}));
144+
REQUIRE_THROWS_HR(pathTree.FindOrInsert({}), E_INVALIDARG);
145+
}
146+
147+
size_t CountVisited(const PathTree<bool>& pathTree, const std::filesystem::path& path, std::function<bool(const bool&)> predicate)
148+
{
149+
size_t result = 0;
150+
pathTree.VisitIf(path, [&](const bool&) { ++result; }, predicate);
151+
return result;
152+
}
153+
154+
TEST_CASE("PathTree_VisitIf_Count", "[filesystem][pathtree]")
155+
{
156+
PathTree<bool> pathTree;
157+
158+
pathTree.FindOrInsert(L"C:\\a\\aa\\aaa") = true;
159+
pathTree.FindOrInsert(L"C:\\a\\aa\\bbb") = true;
160+
pathTree.FindOrInsert(L"C:\\a\\aa\\ccc") = false;
161+
pathTree.FindOrInsert(L"C:\\a\\aa") = true;
162+
163+
pathTree.FindOrInsert(L"C:\\a\\bb\\aaa") = false;
164+
pathTree.FindOrInsert(L"C:\\a\\bb\\bbb") = true;
165+
pathTree.FindOrInsert(L"C:\\a\\bb\\ccc") = false;
166+
pathTree.FindOrInsert(L"C:\\a\\bb") = true;
167+
168+
pathTree.FindOrInsert(L"C:\\a\\cc\\aaa") = true;
169+
pathTree.FindOrInsert(L"C:\\a\\cc\\bbb") = false;
170+
pathTree.FindOrInsert(L"C:\\a\\cc\\ccc") = false;
171+
pathTree.FindOrInsert(L"C:\\a\\cc") = false;
172+
173+
pathTree.FindOrInsert(L"C:\\a") = true;
174+
pathTree.FindOrInsert(L"C:\\b") = false;
175+
pathTree.FindOrInsert(L"D:\\a") = false;
176+
177+
auto always = [](const bool&) { return true; };
178+
auto never = [](const bool&) { return false; };
179+
auto if_input = [](const bool& b) { return b; };
180+
181+
REQUIRE(0 == CountVisited(pathTree, {}, always));
182+
183+
REQUIRE(15 == CountVisited(pathTree, L"C:\\", always));
184+
REQUIRE(2 == CountVisited(pathTree, L"D:\\", always));
185+
REQUIRE(0 == CountVisited(pathTree, L"E:\\", always));
186+
187+
REQUIRE(1 == CountVisited(pathTree, L"C:\\", never));
188+
REQUIRE(1 == CountVisited(pathTree, L"D:\\", never));
189+
REQUIRE(0 == CountVisited(pathTree, L"E:\\", never));
190+
191+
REQUIRE(7 == CountVisited(pathTree, L"C:\\", if_input));
192+
REQUIRE(6 == CountVisited(pathTree, L"C:\\a", if_input));
193+
REQUIRE(2 == CountVisited(pathTree, L"C:\\a\\cc", if_input));
194+
REQUIRE(1 == CountVisited(pathTree, L"D:\\", if_input));
195+
REQUIRE(0 == CountVisited(pathTree, L"E:\\", if_input));
196+
}
197+
198+
TEST_CASE("PathTree_VisitIf_Correct", "[filesystem][pathtree]")
199+
{
200+
PathTree<std::pair<bool, bool>> pathTree;
201+
202+
pathTree.FindOrInsert(L"C:\\a\\aa\\aaa") = { true, true };
203+
pathTree.FindOrInsert(L"C:\\a\\aa\\bbb") = { true, true };
204+
pathTree.FindOrInsert(L"C:\\a\\aa\\ccc") = { false, false };
205+
pathTree.FindOrInsert(L"C:\\a\\aa") = { true, true };
206+
207+
pathTree.FindOrInsert(L"C:\\a\\bb\\aaa") = { false, false };
208+
pathTree.FindOrInsert(L"C:\\a\\bb\\bbb") = { true, true };
209+
pathTree.FindOrInsert(L"C:\\a\\bb\\ccc") = { false, false };
210+
pathTree.FindOrInsert(L"C:\\a\\bb") = { true, true };
211+
212+
pathTree.FindOrInsert(L"C:\\a\\cc\\aaa") = { true, true };
213+
pathTree.FindOrInsert(L"C:\\a\\cc\\bbb") = { false, false };
214+
pathTree.FindOrInsert(L"C:\\a\\cc\\ccc") = { false, false };
215+
pathTree.FindOrInsert(L"C:\\a\\cc") = { false, false };
216+
217+
pathTree.FindOrInsert(L"C:\\a") = { true, true };
218+
pathTree.FindOrInsert(L"C:\\b") = { false, false };
219+
pathTree.FindOrInsert(L"C:") = { true, false };
220+
221+
auto check_input = [](const std::pair<bool, bool>& p) { REQUIRE(p.first); };
222+
auto if_input = [](const std::pair<bool, bool>& p) { return p.second; };
223+
224+
pathTree.VisitIf(L"C:", check_input, if_input);
225+
}

src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@
346346
<ClInclude Include="Public\winget\LocIndependent.h" />
347347
<ClInclude Include="Public\winget\ManagedFile.h" />
348348
<ClInclude Include="Public\winget\ModuleCountBase.h" />
349+
<ClInclude Include="Public\winget\PathTree.h" />
349350
<ClInclude Include="Public\winget\Registry.h" />
350351
<ClInclude Include="Public\winget\Resources.h" />
351352
<ClInclude Include="Public\winget\Runtime.h" />

src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@
146146
<ClInclude Include="Public\winget\DetectMismatch.h">
147147
<Filter>Public\winget</Filter>
148148
</ClInclude>
149+
<ClInclude Include="Public\winget\PathTree.h">
150+
<Filter>Public\winget</Filter>
151+
</ClInclude>
149152
</ItemGroup>
150153
<ItemGroup>
151154
<ClCompile Include="pch.cpp">

0 commit comments

Comments
 (0)