Hosts file editor (#20462)

This commit is contained in:
Davide Giacometti
2022-10-13 13:05:43 +02:00
committed by GitHub
parent ab41b61e84
commit b2e1337d4e
67 changed files with 3858 additions and 13 deletions

View File

@@ -0,0 +1,83 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Hosts.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Hosts.Tests
{
[TestClass]
public class EntryTest
{
[DataTestMethod]
[DataRow("\t\t10.1.1.1\t\thost\t\t", "10.1.1.1", "host", "", true)]
[DataRow(" 10.1.1.1 host ", "10.1.1.1", "host", "", true)]
[DataRow("10.1.1.1 host", "10.1.1.1", "host", "", true)]
[DataRow("\t\t#\t\t10.1.1.1\thost\t\t", "10.1.1.1", "host", "", false)]
[DataRow(" # 10.1.1.1 host ", "10.1.1.1", "host", "", false)]
[DataRow("#10.1.1.1 host", "10.1.1.1", "host", "", false)]
[DataRow("\t\t10.1.1.1\t\thost\t\t#\t\tcomment\t\t", "10.1.1.1", "host", "comment", true)]
[DataRow(" 10.1.1.1 host # comment ", "10.1.1.1", "host", "comment", true)]
[DataRow("10.1.1.1 host#comment", "10.1.1.1", "host", "comment", true)]
[DataRow("\t\t#\t\t10.1.1.1\thost\t\t#\t\tcomment\t\t", "10.1.1.1", "host", "comment", false)]
[DataRow(" # 10.1.1.1 host # comment ", "10.1.1.1", "host", "comment", false)]
[DataRow("#10.1.1.1 host#comment", "10.1.1.1", "host", "comment", false)]
[DataRow("# #10.1.1.1 host#comment", "10.1.1.1", "host", "comment", false)]
[DataRow("# #\t10.1.1.1 host#comment", "10.1.1.1", "host", "comment", false)]
[DataRow("# # \t10.1.1.1 host#comment", "10.1.1.1", "host", "comment", false)]
public void Valid_Entry_SingleHost(string line, string address, string host, string comment, bool active)
{
var entry = new Entry(line);
Assert.AreEqual(entry.Address, address);
Assert.AreEqual(entry.Hosts, host);
Assert.AreEqual(entry.Comment, comment);
Assert.AreEqual(entry.Active, active);
Assert.IsTrue(entry.Valid);
}
[DataTestMethod]
[DataRow("\t\t10.1.1.1\t\thost host.local\t\t", "10.1.1.1", "host host.local", "", true)]
[DataRow(" 10.1.1.1 host host.local ", "10.1.1.1", "host host.local", "", true)]
[DataRow("10.1.1.1 host host.local", "10.1.1.1", "host host.local", "", true)]
[DataRow("\t\t#\t\t10.1.1.1\thost\t\thost.local\t\t", "10.1.1.1", "host host.local", "", false)]
[DataRow(" # 10.1.1.1 host host.local ", "10.1.1.1", "host host.local", "", false)]
[DataRow("#10.1.1.1 host host.local", "10.1.1.1", "host host.local", "", false)]
[DataRow("\t\t10.1.1.1\t\thost\t\thost.local\t\t#\t\tcomment\t\t", "10.1.1.1", "host host.local", "comment", true)]
[DataRow(" 10.1.1.1 host host.local # comment ", "10.1.1.1", "host host.local", "comment", true)]
[DataRow("10.1.1.1 host host.local#comment", "10.1.1.1", "host host.local", "comment", true)]
[DataRow("\t\t#\t\t10.1.1.1\thost\t\thost.local\t\t#\t\tcomment\t\t", "10.1.1.1", "host host.local", "comment", false)]
[DataRow(" # 10.1.1.1 host host.local # comment ", "10.1.1.1", "host host.local", "comment", false)]
[DataRow("#10.1.1.1 host host.local#comment", "10.1.1.1", "host host.local", "comment", false)]
public void Valid_Entry_MultipleHosts(string line, string address, string host, string comment, bool active)
{
var entry = new Entry(line);
Assert.AreEqual(entry.Address, address);
Assert.AreEqual(entry.Hosts, host);
Assert.AreEqual(entry.Comment, comment);
Assert.AreEqual(entry.Active, active);
Assert.IsTrue(entry.Valid);
}
[DataTestMethod]
[DataRow("\t\t10.1.1.1\t\t")]
[DataRow(" 10.1.1.1 ")]
[DataRow("10.1.1.1")]
[DataRow("\t\thost\t\t")]
[DataRow(" host ")]
[DataRow("host")]
[DataRow("\t\t10\t\thost")]
[DataRow(" 10 host ")]
[DataRow("10 host")]
[DataRow("\t\thost\t\t10.1.1.1")]
[DataRow(" host 10.1.1.1")]
[DataRow("host 10.1.1.1")]
public void Not_Valid_Entry(string line)
{
var entry = new Entry(line);
Assert.IsFalse(entry.Valid);
}
}
}

View File

@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\Version.props" />
<PropertyGroup>
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
<RuntimeIdentifiers>win10-x64;win10-arm64</RuntimeIdentifiers>
<IsPackable>false</IsPackable>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\modules\Hosts\Hosts.Tests\</OutputPath>
<IntermediateOutputPath>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(AssemblyName)\</IntermediateOutputPath>
<RootNamespace>Hosts.Tests</RootNamespace>
<AssemblyName>PowerToys.Hosts.Tests</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.3" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.3" />
<PackageReference Include="System.IO.Abstractions" Version="12.2.5" />
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="12.2.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Hosts\Hosts.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,255 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.IO.Abstractions.TestingHelpers;
using System.Linq;
using System.Threading.Tasks;
using Hosts.Helpers;
using Hosts.Models;
using Hosts.Settings;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Settings.UI.Library.Enumerations;
namespace Hosts.Tests
{
[TestClass]
public class HostsServiceTest
{
private static Mock<IElevationHelper> _elevationHelper;
[ClassInitialize]
public static void ClassInitialize(TestContext context)
{
_elevationHelper = new Mock<IElevationHelper>();
_elevationHelper.Setup(m => m.IsElevated).Returns(true);
}
[TestMethod]
public void Hosts_Exists()
{
var fileSystem = new MockFileSystem
{
FileSystemWatcher = new TestFileSystemWatcherFactory(),
};
var userSettings = new Mock<IUserSettings>();
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
fileSystem.AddFile(service.HostsFilePath, new MockFileData(string.Empty));
var result = service.Exists();
Assert.IsTrue(result);
}
[TestMethod]
public void Hosts_Not_Exists()
{
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
{
})
{
FileSystemWatcher = new TestFileSystemWatcherFactory(),
};
var userSettings = new Mock<IUserSettings>();
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
var result = service.Exists();
Assert.IsFalse(result);
}
[TestMethod]
public async Task Host_Added()
{
var content =
@"10.1.1.1 host host.local # comment
10.1.1.2 host2 host2.local # another comment
";
var contentResult =
@" 10.1.1.1 host host.local # comment
10.1.1.2 host2 host2.local # another comment
# 10.1.1.30 host30 host30.local # new entry
";
var fileSystem = new MockFileSystem
{
FileSystemWatcher = new TestFileSystemWatcherFactory(),
};
var userSettings = new Mock<IUserSettings>();
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
fileSystem.AddFile(service.HostsFilePath, new MockFileData(content));
var (_, entries) = await service.ReadAsync();
entries.Add(new Entry("10.1.1.30", "host30 host30.local", "new entry", false));
await service.WriteAsync(string.Empty, entries);
var result = fileSystem.GetFile(service.HostsFilePath);
Assert.AreEqual(result.TextContents, contentResult);
}
[TestMethod]
public async Task Host_Deleted()
{
var content =
@"10.1.1.1 host host.local # comment
10.1.1.2 host2 host2.local # another comment
";
var contentResult =
@"10.1.1.2 host2 host2.local # another comment
";
var fileSystem = new MockFileSystem
{
FileSystemWatcher = new TestFileSystemWatcherFactory(),
};
var userSettings = new Mock<IUserSettings>();
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
fileSystem.AddFile(service.HostsFilePath, new MockFileData(content));
var (_, entries) = await service.ReadAsync();
entries.RemoveAt(0);
await service.WriteAsync(string.Empty, entries);
var result = fileSystem.GetFile(service.HostsFilePath);
Assert.AreEqual(result.TextContents, contentResult);
}
[TestMethod]
public async Task Host_Updated()
{
var content =
@"10.1.1.1 host host.local # comment
10.1.1.2 host2 host2.local # another comment
";
var contentResult =
@"# 10.1.1.10 host host.local host1.local # updated comment
10.1.1.2 host2 host2.local # another comment
";
var fileSystem = new MockFileSystem
{
FileSystemWatcher = new TestFileSystemWatcherFactory(),
};
var userSettings = new Mock<IUserSettings>();
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
fileSystem.AddFile(service.HostsFilePath, new MockFileData(content));
var (_, entries) = await service.ReadAsync();
var entry = entries[0];
entry.Address = "10.1.1.10";
entry.Hosts = "host host.local host1.local";
entry.Comment = "updated comment";
entry.Active = false;
await service.WriteAsync(string.Empty, entries);
var result = fileSystem.GetFile(service.HostsFilePath);
Assert.AreEqual(result.TextContents, contentResult);
}
[TestMethod]
public async Task Empty_Hosts()
{
var fileSystem = new MockFileSystem
{
FileSystemWatcher = new TestFileSystemWatcherFactory(),
};
var userSettings = new Mock<IUserSettings>();
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
fileSystem.AddFile(service.HostsFilePath, new MockFileData(string.Empty));
await service.WriteAsync(string.Empty, Enumerable.Empty<Entry>());
var result = fileSystem.GetFile(service.HostsFilePath);
Assert.AreEqual(result.TextContents, string.Empty);
}
[TestMethod]
public async Task AdditionalLines_Top()
{
var content =
@"# header
10.1.1.1 host host.local # comment
# comment
10.1.1.2 host2 host2.local # another comment
# footer
";
var contentResult =
@"# header
# comment
# footer
10.1.1.1 host host.local # comment
10.1.1.2 host2 host2.local # another comment
";
var fileSystem = new MockFileSystem
{
FileSystemWatcher = new TestFileSystemWatcherFactory(),
};
var userSettings = new Mock<IUserSettings>();
userSettings.Setup(m => m.AdditionalLinesPosition).Returns(AdditionalLinesPosition.Top);
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
fileSystem.AddFile(service.HostsFilePath, new MockFileData(content));
var (additionalLines, entries) = await service.ReadAsync();
await service.WriteAsync(additionalLines, entries);
var result = fileSystem.GetFile(service.HostsFilePath);
Assert.AreEqual(result.TextContents, contentResult);
}
[TestMethod]
public async Task AdditionalLines_Bottom()
{
var content =
@"# header
10.1.1.1 host host.local # comment
# comment
10.1.1.2 host2 host2.local # another comment
# footer
";
var contentResult =
@"10.1.1.1 host host.local # comment
10.1.1.2 host2 host2.local # another comment
# header
# comment
# footer
";
var fileSystem = new MockFileSystem
{
FileSystemWatcher = new TestFileSystemWatcherFactory(),
};
var userSettings = new Mock<IUserSettings>();
userSettings.Setup(m => m.AdditionalLinesPosition).Returns(AdditionalLinesPosition.Bottom);
var service = new HostsService(fileSystem, userSettings.Object, _elevationHelper.Object);
fileSystem.AddFile(service.HostsFilePath, new MockFileData(content));
var (additionalLines, entries) = await service.ReadAsync();
await service.WriteAsync(additionalLines, entries);
var result = fileSystem.GetFile(service.HostsFilePath);
Assert.AreEqual(result.TextContents, contentResult);
}
}
}

View File

@@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.ComponentModel;
using System.IO;
using System.IO.Abstractions;
namespace Hosts.Tests
{
public class TestFileSystemWatcher : FileSystemWatcherBase
{
public override bool IncludeSubdirectories { get; set; }
public override bool EnableRaisingEvents { get; set; }
public override string Filter { get; set; }
public override int InternalBufferSize { get; set; }
public override NotifyFilters NotifyFilter { get; set; }
public override string Path { get; set; }
public override ISite Site { get; set; }
public override ISynchronizeInvoke SynchronizingObject { get; set; }
public override WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) => default(WaitForChangedResult);
public override WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) => default(WaitForChangedResult);
public TestFileSystemWatcher(string path) => Path = path;
public TestFileSystemWatcher(string path, string filter)
{
Path = path;
Filter = filter;
}
public override void BeginInit()
{
}
public override void EndInit()
{
}
}
}

View File

@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.IO.Abstractions;
namespace Hosts.Tests
{
public class TestFileSystemWatcherFactory : IFileSystemWatcherFactory
{
public IFileSystemWatcher CreateNew() => new TestFileSystemWatcher(null);
public IFileSystemWatcher CreateNew(string path) => new TestFileSystemWatcher(path);
public IFileSystemWatcher CreateNew(string path, string filter) => new TestFileSystemWatcher(path, filter);
public IFileSystemWatcher FromPath(string path) => new TestFileSystemWatcher(path);
}
}