diff --git a/.gitignore b/.gitignore
index e810d394..25a04d1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -173,6 +173,7 @@ PublishScripts/
# NuGet Packages
*.nupkg
+/StrawberryPerl.5.28.0.1/
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
diff --git a/Build/Local/Nuget/nuget.exe b/Build/Local/Nuget/nuget.exe
new file mode 100644
index 00000000..d6ecf835
Binary files /dev/null and b/Build/Local/Nuget/nuget.exe differ
diff --git a/Directory.Build.props b/Directory.Build.props
index 01f3960c..00d7f92b 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -11,7 +11,10 @@
Enlistment root is based off of wherever this file is. Be sure not to set this property anywhere else.
-->
$(MSBuildThisFileDirectory.TrimEnd('\\'))
- net472
+
+ $([MSBuild]::IsOSPlatform('Windows'))
+ net472
diff --git a/SmoBuild/DdlEvents.targets b/SmoBuild/DdlEvents.targets
index 8e4fb1eb..a3cc3912 100644
--- a/SmoBuild/DdlEvents.targets
+++ b/SmoBuild/DdlEvents.targets
@@ -1,14 +1,18 @@
$(MSBuildAllProjects);$(MSBuildThisFileFullPath)
- $(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework)
+ $(BaseIntermediateOutputPath)$(Configuration)/$(TargetFramework)
- c:\strawberry\perl\bin\perl.exe
- $(EnlistmentRoot)\packages\StrawberryPerl.5.28.0.1\bin\perl.exe
+ c:\strawberry\perl\bin\perl.exe
+ $(EnlistmentRoot)\packages\StrawberryPerl.5.28.0.1\bin\perl.exe
+ $(EnlistmentRoot)\StrawberryPerl.5.28.0.1\bin\perl.exe
+ $(EnlistmentRoot)\packages\StrawberryPerl.5.28.0.1\bin\perl.exe
+
+ perl
@@ -26,12 +30,12 @@
BeforeTargets="ResolveReferences"
Inputs="$(MSBuildThisFileFullPath);@(DdlEventsGenCompile)"
Outputs="@(EventsFileName->'$(SmoCodeGenOutputDir)\%(CodeFile).cs')" >
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/SmoBuild/DdlEvents/evtschema.pl b/SmoBuild/DdlEvents/evtschema.pl
index d40396fa..47be1772 100644
--- a/SmoBuild/DdlEvents/evtschema.pl
+++ b/SmoBuild/DdlEvents/evtschema.pl
@@ -22,7 +22,7 @@
open(EVTSCHEMACPP, $eventsschema_cpp) || die "Cannot open $eventsschema_cpp\n";
# New destination file
-open(OUTF, ">" . $OutputDir . "\\eventsschema.tmp") || die "Cannot write to eventsschema.tmp\n";
+open(OUTF, ">" . $OutputDir . "/eventsschema.tmp") || die "Cannot write to eventsschema.tmp\n";
# read and copy
while()
@@ -46,7 +46,7 @@
sub IncludeFile
{
local($IncName) = @_;
- open(INCFILE, $OutputDir . "\\" . $IncName) || die "Cannot open " . $IncName . "\n";
+ open(INCFILE, $OutputDir . "/" . $IncName) || die "Cannot open " . $IncName . "\n";
while()
{
print OUTF $_;
diff --git a/SmoBuild/DdlEvents/trc2evt.pl b/SmoBuild/DdlEvents/trc2evt.pl
index 33333cd5..342316e3 100644
--- a/SmoBuild/DdlEvents/trc2evt.pl
+++ b/SmoBuild/DdlEvents/trc2evt.pl
@@ -303,7 +303,7 @@
# print event group enumeration
if($option == 0)
{
- open(FOUT, ">" . $OutputDir . "\\grpenum.inc") || die "Cannot write to " . $OutputDir . "\\grpenum.inc\n";
+ open(FOUT, ">" . $OutputDir . "/grpenum.inc") || die "Cannot write to " . $OutputDir . "/grpenum.inc\n";
print FOUT "// Extended Event Group for SQLTrace\n";
print FOUT "enum ETraceEventGroup {\n";
print FOUT "\tEGROUP_TRCAT_START = EGROUP_TRCAT_ALL, // Setting beginning\n";
@@ -318,7 +318,7 @@
#
# print event group name
- open(FOUT, ">" . $OutputDir . "\\grpdefs.inc") || die "Cannot write to " . $OutputDir . "\\grpdefs.inc\n";
+ open(FOUT, ">" . $OutputDir . "/grpdefs.inc") || die "Cannot write to " . $OutputDir . "/grpdefs.inc\n";
print FOUT "// Extended Event Groups for SQLTrace\n";
for (my $i = 1; $i <= $#rgsCategoryIDs; $i++)
{
@@ -330,7 +330,7 @@
#
# print paraminfo array
- open(FOUT, ">" . $OutputDir . "\\parinfo.inc") || die "Cannot write to " . $OutputDir . "\\parinfo.inc\n";
+ open(FOUT, ">" . $OutputDir . "/parinfo.inc") || die "Cannot write to " . $OutputDir . "/parinfo.inc\n";
my $ColumnCount = @rgsColumnNames;
print FOUT "//----------------------------------------------------------------\n";
print FOUT "// TraceEventTag Enum\n";
@@ -400,7 +400,7 @@
#
# print schema array
- open(FOUT, ">" . $OutputDir . "\\schema.inc") || die "Cannot write to " . $OutputDir . "\\schema.inc\n";
+ open(FOUT, ">" . $OutputDir . "/schema.inc") || die "Cannot write to " . $OutputDir . "/schema.inc\n";
print FOUT "// Schema array extended for SQLTrace\n";
for (my $i = $EventClassStart; $i <= $#rgsEventIDs; $i++)
{
@@ -447,7 +447,7 @@
#
# Generate event hierarchy graph for reference. This file is not used for compilation.
- open(FOUT, ">" . $OutputDir . "\\hierarchy.txt") || die "Cannot write to " . $OutputDir . "\\hierarchy.txt.\n";
+ open(FOUT, ">" . $OutputDir . "/hierarchy.txt") || die "Cannot write to " . $OutputDir . "/hierarchy.txt.\n";
print FOUT "========== SQL Trace event hierarchy ==================\n\n";
print FOUT "ALL_EVENTS\n";
print FOUT "|____ TRC_ALL_EVENTS\n";
@@ -483,8 +483,8 @@
# Form more information, take a look at:
# http://blogs.msdn.com/brettsh/archive/2006/06/07/620986.aspx
# ----------------------------------------------------------------
- open(FOUT, ">:raw:encoding(UTF16-LE):crlf:utf8", $OutputDir . "\\etwcls.mof")
- || die "Cannot write to " . $OutputDir . "\\etwcls.mof\n";
+ open(FOUT, ">:raw:encoding(UTF16-LE):crlf:utf8", $OutputDir . "/etwcls.mof")
+ || die "Cannot write to " . $OutputDir . "/etwcls.mof\n";
print FOUT "\x{FEFF}"; # print BOM (Byte Order Mark) for the unicode file
# ----------------------------------------------------------------
@@ -548,7 +548,7 @@
#
# GUID include file which lists all ETW event class GUIDs
#
- open(FOUT, ">" .$OutputDir . "\\etwguid.inc") || die "Cannot write to " . $OutputDir . "\\etwguid.inc\n";
+ open(FOUT, ">" .$OutputDir . "/etwguid.inc") || die "Cannot write to " . $OutputDir . "/etwguid.inc\n";
print FOUT "// Array of Event Class Info\n\n";
for (my $i = $EventClassStart; $i <= $#rgsEventIDs; $i++)
@@ -566,7 +566,7 @@
# Dump All Columns to XML
#
- open(FOUT, ">" .$OutputDir . "\\events_trace_template.xst") || die "Cannot write to " . $OutputDir . "\\events_trace_template.xst\n";
+ open(FOUT, ">" .$OutputDir . "/events_trace_template.xst") || die "Cannot write to " . $OutputDir . "/events_trace_template.xst\n";
for (my $col = 1; $col < $ColumnCount; $col++)
{
#print param defs for active cols except for SPID/EventClass
@@ -585,7 +585,7 @@
# 2: instance guid (INSTGUID)
if($option == 1)
{
- open(FOUT, ">" . $OutputDir . "\\etwsetup.inc") || die "Cannot write to " . $OutputDir . "\\etwsetup.inc\n";
+ open(FOUT, ">" . $OutputDir . "/etwsetup.inc") || die "Cannot write to " . $OutputDir . "/etwsetup.inc\n";
print FOUT "{NOPARAM, L\"// ************************************************************************\\n\"},\n";
print FOUT "{NOPARAM, L\"// \\tCopyrights (c) Microsoft Corporation\\n//\\n\"},\n";
print FOUT "{NOPARAM, L\"// ************************************************************************\\n\\n\"},\n";
diff --git a/init.cmd b/init.cmd
index bb1d5bb2..9c6a4d07 100644
--- a/init.cmd
+++ b/init.cmd
@@ -28,7 +28,7 @@ doskey prod=pushd %BASEDIR%src\Microsoft\SqlServer\Management\$*
doskey sfc=pushd %BASEDIR%src\Microsoft\SqlServer\Management\Sdk\sfc
doskey smo=pushd %BASEDIR%src\Microsoft\SqlServer\Management\Smo
doskey smoenum=pushd %BASEDIR%src\Microsoft\SqlServer\Management\SqlEnum\$*
-doskey clean=powershell.exe -ExecutionPolicy Unrestricted -File "%BASEDIR%init.ps1" -Clean
+
doskey tst=pushd %BASEDIR%src\FunctionalTest\Smo\$*
doskey slngen19=slngen -vs "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\devenv.exe" $*
@@ -44,16 +44,30 @@ doskey netcoretests=pushd %BASEDIR%bin\debug\net8.0$Tvstest.console.exe microsof
title git %BASEDIR%
-REM Migration to PowerShell environment
-powershell.exe -ExecutionPolicy Unrestricted -File "%BASEDIR%init.ps1" -Initialize
-if "%errorlevel%" neq "0" (
- echo Failed to setup local dev build environment correctly
+REM Read the required SDK version from global.json so this script never drifts from it
+for /f "usebackq tokens=2 delims=:, " %%V in (`findstr /r /c:"\"version\"" %BASEDIR%global.json`) do set DOTNET_SDK_VERSION=%%~V
+set DOTNET_SDK_VERSION=%DOTNET_SDK_VERSION:"=%
+dotnet --list-sdks | findstr /b /c:"%DOTNET_SDK_VERSION% " >nul
+IF ERRORLEVEL 1 (
+ echo .NET SDK %DOTNET_SDK_VERSION% ^(required by global.json^) was not found.
+ echo Install it from https://dotnet.microsoft.com/download/dotnet/%DOTNET_SDK_VERSION:~0,-4%
)
dotnet tool install --global Microsoft.VisualStudio.SlnGen.Tool
dotnet tool install --global Microsoft.SqlPackage
+
+set NUGET_EXE=%BASEDIR%Build\Local\Nuget\nuget.exe
+IF NOT EXIST %NUGET_EXE% (
+ echo nuget.exe not found, downloading from https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
+ IF NOT EXIST %BASEDIR%Build\Local\Nuget mkdir %BASEDIR%Build\Local\Nuget
+ powershell -NoProfile -ExecutionPolicy Bypass -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri 'https://dist.nuget.org/win-x86-commandline/latest/nuget.exe' -OutFile '%NUGET_EXE%'"
+ IF NOT EXIST %NUGET_EXE% (
+ echo Failed to download nuget.exe. Please download it manually from https://dist.nuget.org/win-x86-commandline/latest/nuget.exe and place it at %NUGET_EXE%
+ goto :EOF
+ )
+)
IF NOT EXIST %BASEDIR%packages\StrawberryPerl.5.28.0.1\bin\perl.exe (
- %BASEDIR%Build\Local\Nuget\nuget.exe install StrawberryPerl -Version 5.28.0.1
+ %NUGET_EXE% install StrawberryPerl -Version 5.28.0.1
)
echo.
diff --git a/init.sh b/init.sh
new file mode 100644
index 00000000..c95cb2e6
--- /dev/null
+++ b/init.sh
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+#
+# Setup the developer environment on Linux (e.g. Ubuntu).
+# **This script is not run on build machines**
+#
+# Use this script to idempotently install the tools needed to build dirs.proj on Linux.
+#
+# Notes:
+# - net472 (the .NET Framework) cannot be built or run on Linux, so a Linux build only
+# produces the netstandard2.0/net8.0/net10.0 targets. This is handled automatically
+# by the MSBuild props (see $(IsWindowsBuild) in Directory.Build.props) - you don't
+# need to pass any extra flags.
+# - Build with: dotnet build "$BASEDIR/dirs.proj"
+# - Run unit tests for a project with: dotnet test
+#
+set -euo pipefail
+
+BASEDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+DOTNET_SDK_VERSION="$(grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' "$BASEDIR/global.json" | head -1 | sed -E 's/.*"([^"]+)"$/\1/')"
+if [ -z "$DOTNET_SDK_VERSION" ]; then
+ echo "Error: could not read sdk.version from $BASEDIR/global.json" >&2
+ exit 1
+fi
+DOTNET_INSTALL_DIR="${DOTNET_INSTALL_DIR:-$HOME/.dotnet}"
+
+echo "Enlistment root: $BASEDIR"
+
+# ---------------------------------------------------------------------------
+# perl - required to generate DDL event code (SmoBuild/DdlEvents/*.pl)
+# ---------------------------------------------------------------------------
+if ! command -v perl >/dev/null 2>&1; then
+ echo "perl not found, installing..."
+ if command -v apt-get >/dev/null 2>&1; then
+ sudo apt-get update
+ sudo apt-get install -y perl
+ else
+ echo "Warning: apt-get not found. Please install perl manually and re-run this script." >&2
+ fi
+else
+ echo "perl already installed: $(command -v perl)"
+fi
+
+# ---------------------------------------------------------------------------
+# .NET SDK - required to build and test the repo
+# ---------------------------------------------------------------------------
+has_required_sdk() {
+ command -v dotnet >/dev/null 2>&1 && dotnet --list-sdks 2>/dev/null | grep -q "^${DOTNET_SDK_VERSION} "
+}
+
+if has_required_sdk; then
+ echo ".NET SDK $DOTNET_SDK_VERSION already installed: $(command -v dotnet)"
+else
+ echo ".NET SDK $DOTNET_SDK_VERSION not found, installing to $DOTNET_INSTALL_DIR..."
+ curl -sSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh
+ bash /tmp/dotnet-install.sh --version "$DOTNET_SDK_VERSION" --install-dir "$DOTNET_INSTALL_DIR"
+ rm -f /tmp/dotnet-install.sh
+
+ export PATH="$DOTNET_INSTALL_DIR:$PATH"
+ if ! grep -q "$DOTNET_INSTALL_DIR" "$HOME/.bashrc" 2>/dev/null; then
+ {
+ echo ""
+ echo "# Added by init.sh for sqlmanagementobjects"
+ echo "export DOTNET_ROOT=\"$DOTNET_INSTALL_DIR\""
+ echo "export PATH=\"$DOTNET_INSTALL_DIR:\$PATH\""
+ } >> "$HOME/.bashrc"
+ echo "Added $DOTNET_INSTALL_DIR to PATH in ~/.bashrc (start a new shell, or run 'source ~/.bashrc')."
+ fi
+fi
+
+# ---------------------------------------------------------------------------
+# dotnet global tools used by the repo
+# ---------------------------------------------------------------------------
+install_global_tool_if_missing() {
+ local tool_name="$1"
+ if dotnet tool list --global 2>/dev/null | grep -qi "^${tool_name} "; then
+ echo "$tool_name already installed as a global dotnet tool"
+ else
+ dotnet tool install --global "$tool_name"
+ fi
+}
+
+install_global_tool_if_missing "Microsoft.VisualStudio.SlnGen.Tool"
+install_global_tool_if_missing "Microsoft.SqlPackage"
+
+echo
+echo
+echo "To build for SMO development and to run unit tests:"
+echo " dotnet build $BASEDIR/dirs.proj"
+echo
+echo "Note: net472 (the .NET Framework) is skipped on Linux; the build produces"
+echo " the netstandard2.0/net8.0/net10.0 targets only."
+echo
+echo "To run tests for a single test project:"
+echo " dotnet test $BASEDIR/src/UnitTest/Smo/Microsoft.SqlServer.Test.SmoUnitTests.csproj"
+echo
+echo "To run functional tests, see $BASEDIR/src/FunctionalTest/runtests.cmd for the list"
+echo "of filters and environment variables (e.g. SqlTestTargetServersFilter) that are"
+echo "used to select which tests/backends to run, then invoke with dotnet test."
diff --git a/src/Codegen/CodeGen.cs b/src/Codegen/CodeGen.cs
index 4de88f80..0583d7e7 100644
--- a/src/Codegen/CodeGen.cs
+++ b/src/Codegen/CodeGen.cs
@@ -436,7 +436,7 @@ public static int Main(string[] args)
string output_dir = "";
if (args.Length == 4)
{
- output_dir = args[3] + "\\";
+ output_dir = args[3];
}
bool bGenerateNewFile = false;
@@ -724,7 +724,7 @@ static void GenerateClass(XmlNode node, string node_namespace, string output_dir
bool bGenMetadata = Boolean.Parse(GetAttribute("gen_metadata", node, "true"));
string sUrn = GetAttribute("urn", node, null);
- string modelOutputDir = output_dir + "model\\";
+ string modelOutputDir = Path.Combine(output_dir, "model");
if (!String.IsNullOrEmpty(output_dir) && !Directory.Exists(output_dir))
{
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 566bf5e7..ced97ced 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -5,7 +5,7 @@
bin
- $(EnlistmentRoot)\$(TargetFolder)\gac
+ $(EnlistmentRoot)/$(TargetFolder)/gac
false
@@ -14,12 +14,12 @@
$(MSBuildAllProjects);$(MSBuildThisFileFullPath)
Debug
- $(EnlistmentRoot)\$(TargetFolder)
- $(BaseOutputPath)\$(Configuration)
+ $(EnlistmentRoot)/$(TargetFolder)
+ $(BaseOutputPath)/$(Configuration)
- $(EnlistmentRoot)\obj\$([MSBuild]::MakeRelative('$(EnlistmentRoot)\', $(MSBuildProjectDirectory)))
+ $(EnlistmentRoot)/obj/$([MSBuild]::MakeRelative('$(EnlistmentRoot)/', $(MSBuildProjectDirectory)))
- $(EnlistmentRoot)\SmoBuild\
+ $(EnlistmentRoot)/SmoBuild/
netcore
netfx
CSharp.NetFx.props
@@ -28,7 +28,7 @@
$(NoWarn);NU5125;MSB3026;CA1416;CS1668;NU1903
true
- $(EnlistmentRoot)\packages
+ $(EnlistmentRoot)/packages
false
diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets
index adb0b811..04580f55 100644
--- a/src/Directory.Build.targets
+++ b/src/Directory.Build.targets
@@ -38,7 +38,7 @@ public const int MajorVersion = $(AssemblyMajorVersion)%3B
-
+
@@ -46,7 +46,7 @@ public const int MajorVersion = $(AssemblyMajorVersion)%3B
-
+
diff --git a/src/FunctionalTest/Framework/Microsoft.SqlServer.Test.Manageability.Utils.csproj b/src/FunctionalTest/Framework/Microsoft.SqlServer.Test.Manageability.Utils.csproj
index a72e6960..c3652ab4 100644
--- a/src/FunctionalTest/Framework/Microsoft.SqlServer.Test.Manageability.Utils.csproj
+++ b/src/FunctionalTest/Framework/Microsoft.SqlServer.Test.Manageability.Utils.csproj
@@ -34,7 +34,7 @@
- $(BaseOutputPath)\Documentation\$(TargetFramework)\$(AssemblyName).xml
+ $(BaseOutputPath)/Documentation/$(TargetFramework)/$(AssemblyName).xml
$(NoWarn);1591
diff --git a/src/FunctionalTest/Identity/Microsoft.SqlServer.ADO.Identity.csproj b/src/FunctionalTest/Identity/Microsoft.SqlServer.ADO.Identity.csproj
index 162420ce..eb012e83 100644
--- a/src/FunctionalTest/Identity/Microsoft.SqlServer.ADO.Identity.csproj
+++ b/src/FunctionalTest/Identity/Microsoft.SqlServer.ADO.Identity.csproj
@@ -8,7 +8,7 @@
false
- $(BaseOutputPath)\Documentation\$(TargetFramework)\$(AssemblyName).xml
+ $(BaseOutputPath)/Documentation/$(TargetFramework)/$(AssemblyName).xml
Embedded
@@ -18,8 +18,10 @@
-
-
+
+
+
diff --git a/src/Microsoft/Directory.Build.targets b/src/Microsoft/Directory.Build.targets
index e20fcfd7..1adb570a 100644
--- a/src/Microsoft/Directory.Build.targets
+++ b/src/Microsoft/Directory.Build.targets
@@ -2,7 +2,7 @@
- $(BaseOutputPath)\Documentation\$(TargetFramework)\$(AssemblyName).xml
+ $(BaseOutputPath)/Documentation/$(TargetFramework)/$(AssemblyName).xml
$(NoWarn);1591
diff --git a/src/Microsoft/SqlServer/Management/ConnectionInfo/ServerInformation.cs b/src/Microsoft/SqlServer/Management/ConnectionInfo/ServerInformation.cs
index 5cb6d551..4ad8620a 100644
--- a/src/Microsoft/SqlServer/Management/ConnectionInfo/ServerInformation.cs
+++ b/src/Microsoft/SqlServer/Management/ConnectionInfo/ServerInformation.cs
@@ -157,17 +157,9 @@ public bool IsFabricServer
private static readonly HashSet validEditions = new HashSet(Enum.GetValues(typeof(DatabaseEngineEdition)).Cast());
// this query needs to be safe on all platforms. DW and Sql2005 don't support CONNECTIONPROPERTY
- private const string serverVersionQuery = @"DECLARE @edition sysname;
-SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname);
-SELECT case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType',
-@edition AS Edition,
-CONVERT(bigint, SERVERPROPERTY(N'EditionID')) AS EditionID,
-SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition,
-SERVERPROPERTY('ProductVersion') AS ProductVersion,
-@@MICROSOFTVERSION AS MicrosoftVersion,
-case when serverproperty('EngineEdition') = 12 then 1 when serverproperty('EngineEdition') = 11 and @@version like 'Microsoft Azure SQL Data Warehouse%' then 1 else 0 end as IsFabricServer,
-convert(sysname, SERVERPROPERTY(N'Collation')) AS Collation;
-";
+ // Declared as a single line with explicit \r\n so the text is identical regardless of how this
+ // source file's own line endings were checked out or which OS is running.
+ private const string serverVersionQuery = "DECLARE @edition sysname;\r\nSET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname);\r\nSELECT case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType',\r\n@edition AS Edition,\r\nCONVERT(bigint, SERVERPROPERTY(N'EditionID')) AS EditionID,\r\nSERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition,\r\nSERVERPROPERTY('ProductVersion') AS ProductVersion,\r\n@@MICROSOFTVERSION AS MicrosoftVersion,\r\ncase when serverproperty('EngineEdition') = 12 then 1 when serverproperty('EngineEdition') = 11 and @@version like 'Microsoft Azure SQL Data Warehouse%' then 1 else 0 end as IsFabricServer,\r\nconvert(sysname, SERVERPROPERTY(N'Collation')) AS Collation;\r\n";
static public ServerInformation GetServerInformation(IDbConnection sqlConnection, IDbDataAdapter dataAdapter, string serverVersionString)
{
var serverVersion = ParseStringServerVersion(serverVersionString);
@@ -175,18 +167,15 @@ static public ServerInformation GetServerInformation(IDbConnection sqlConnection
if (serverVersion.Major >= 14)
{
- cmdBuilder.AppendLine(@"select host_platform from sys.dm_os_host_info");
+ cmdBuilder.Append("select host_platform from sys.dm_os_host_info\r\n");
}
else
{
// Pre v14 is all Windows
- cmdBuilder.AppendLine(@"select N'Windows' as host_platform");
+ cmdBuilder.Append("select N'Windows' as host_platform\r\n");
}
- cmdBuilder.AppendLine(@"if @edition = N'SQL Azure'
- select 'TCP' as ConnectionProtocol
-else
- exec ('select CONVERT(nvarchar(40),CONNECTIONPROPERTY(''net_transport'')) as ConnectionProtocol')");
+ cmdBuilder.Append("if @edition = N'SQL Azure' \r\n select 'TCP' as ConnectionProtocol\r\nelse\r\n exec ('select CONVERT(nvarchar(40),CONNECTIONPROPERTY(''net_transport'')) as ConnectionProtocol')\r\n");
using (var sqlCommand = sqlConnection.CreateCommand())
{
diff --git a/src/Microsoft/SqlServer/Management/Sdk/Sfc/Enumerator/ObjectLoadInfoManager.cs b/src/Microsoft/SqlServer/Management/Sdk/Sfc/Enumerator/ObjectLoadInfoManager.cs
index 16117031..3915b14a 100644
--- a/src/Microsoft/SqlServer/Management/Sdk/Sfc/Enumerator/ObjectLoadInfoManager.cs
+++ b/src/Microsoft/SqlServer/Management/Sdk/Sfc/Enumerator/ObjectLoadInfoManager.cs
@@ -157,7 +157,7 @@ static private void LoadHierarchy()
m_Hierarchy = new SortedList(System.StringComparer.Ordinal);
#if SMOCODEGEN
- string realFile = Path.Combine(Path.Combine(CodeGenSettings.Directory, "core\\src\\xml"), "Config.xml");
+ string realFile = Path.Combine(CodeGenSettings.Directory, "core", "src", "xml", "Config.xml");
if (!File.Exists(realFile))
{
realFile = Path.Combine(Path.Combine(CodeGenSettings.Directory, "xml"), "Config.xml");
diff --git a/src/Microsoft/SqlServer/Management/Smo/Microsoft.SqlServer.Smo.csproj b/src/Microsoft/SqlServer/Management/Smo/Microsoft.SqlServer.Smo.csproj
index 5252d934..b968f0c4 100644
--- a/src/Microsoft/SqlServer/Management/Smo/Microsoft.SqlServer.Smo.csproj
+++ b/src/Microsoft/SqlServer/Management/Smo/Microsoft.SqlServer.Smo.csproj
@@ -8,7 +8,7 @@
false
$(NoWarn);0169;0414;0649;3021
Microsoft.SqlServer.Management.Smo
- $(BaseIntermediateOutputPath)$(TargetFramework)\Gen
+ $(BaseIntermediateOutputPath)$(TargetFramework)/Gen
.\
true
true
@@ -671,11 +671,11 @@
- "$(OutDir)..\$(CodeGenPlatform)\SmoCodeGen.exe"
- dotnet --roll-forward LatestMajor "$(OutDir)..\$(CodeGenPlatform)\SmoCodeGen.dll"
+ "$(OutDir)../$(CodeGenPlatform)/SmoCodeGen.exe"
+ dotnet --roll-forward LatestMajor "$(OutDir)../$(CodeGenPlatform)/SmoCodeGen.dll"
-
-
+
+
diff --git a/src/Microsoft/SqlServer/Management/SqlAssessment/Microsoft.SqlServer.Management.Assessment.csproj b/src/Microsoft/SqlServer/Management/SqlAssessment/Microsoft.SqlServer.Management.Assessment.csproj
index 6b6e995b..f7cfb171 100644
--- a/src/Microsoft/SqlServer/Management/SqlAssessment/Microsoft.SqlServer.Management.Assessment.csproj
+++ b/src/Microsoft/SqlServer/Management/SqlAssessment/Microsoft.SqlServer.Management.Assessment.csproj
@@ -9,7 +9,7 @@
- "$(OutputPath)\Microsoft.SqlServer.Management.Assessment.xml"
+ "$(OutputPath)/Microsoft.SqlServer.Management.Assessment.xml"
diff --git a/src/Microsoft/SqlServer/Management/SqlEnum/Util.cs b/src/Microsoft/SqlServer/Management/SqlEnum/Util.cs
index 42539b84..7ee59755 100644
--- a/src/Microsoft/SqlServer/Management/SqlEnum/Util.cs
+++ b/src/Microsoft/SqlServer/Management/SqlEnum/Util.cs
@@ -426,7 +426,22 @@ static public string Combine(string path1, string path2, PathType pathType)
}
else if (pathType == PathType.Windows)
{
- return Path.Combine(path1, path2);
+ // Don't use System.IO.Path here: its behavior follows the host OS, but a Windows path
+ // needs to combine the same way whether we're running on Windows or Linux.
+ if (path1.Length == 0)
+ {
+ return path2;
+ }
+ if (path2.Length == 0)
+ {
+ return path1;
+ }
+ if (IsWindowsPathRooted(path2))
+ {
+ return path2;
+ }
+ char lastChar = path1[path1.Length - 1];
+ return (lastChar == '\\' || lastChar == '/') ? path1 + path2 : path1 + '\\' + path2;
}
else // mimic Path.Combine
{
@@ -439,6 +454,44 @@ static public string Combine(string path1, string path2, PathType pathType)
}
}
+ ///
+ /// Mimics Path.IsPathRooted for a Windows-style path, without relying on the host OS's own rules
+ /// (System.IO.Path's behavior on Linux differs from Windows for backslash/drive-letter paths).
+ ///
+ static private bool IsWindowsPathRooted(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ return false;
+ }
+ if (path[0] == '\\' || path[0] == '/')
+ {
+ return true;
+ }
+ return path.Length >= 2 && path[1] == ':' && IsWindowsDriveLetter(path[0]);
+ }
+
+ static private bool IsWindowsDriveLetter(char c)
+ {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+ }
+
+ static private bool IsWindowsUncRoot(string path)
+ {
+ if (path.Length < 2 || (path[0] != '\\' && path[0] != '/') || (path[1] != '\\' && path[1] != '/'))
+ {
+ return false;
+ }
+
+ int serverSeparatorIndex = path.IndexOfAny(['\\', '/'], 2);
+ if (serverSeparatorIndex < 0 || serverSeparatorIndex == path.Length - 1)
+ {
+ return true;
+ }
+
+ return path.IndexOfAny(['\\', '/'], serverSeparatorIndex + 1) < 0;
+ }
+
///
/// Returns the directory string given a path. Handles XI path. It is a simple
/// wrapper so it will be Garbage-In|Garbage-out.
@@ -472,19 +525,45 @@ static public string GetDirectoryName(string s1, PathType pathType)
}
else if (pathType == PathType.Windows)
{
- return Path.GetDirectoryName(s1);
+ // Don't use System.IO.Path here: its behavior follows the host OS, but a Windows path's
+ // directory name needs to be computed the same way whether we're running on Windows or Linux.
+ if (s1.Length == 0)
+ {
+ return s1;
+ }
+ // A bare drive root (e.g. "C:\") has no parent directory
+ if (s1.Length == 3 && IsWindowsDriveLetter(s1[0]) && s1[1] == ':' && (s1[2] == '\\' || s1[2] == '/'))
+ {
+ return null;
+ }
+ if (IsWindowsUncRoot(s1))
+ {
+ return null;
+ }
+ var lastSeparatorIndex = s1.LastIndexOfAny(['\\', '/']);
+ if (lastSeparatorIndex < 0)
+ {
+ return String.Empty;
+ }
+ var dir = s1[..lastSeparatorIndex];
+ // Path.GetDirectoryName keeps the trailing separator for a bare drive root, e.g. "C:\MyDir" -> "C:\"
+ if (dir.Length == 2 && IsWindowsDriveLetter(dir[0]) && dir[1] == ':')
+ {
+ dir += '\\';
+ }
+ return dir;
}
else // mimic Path.GetDirectoryName
{
if (s1 == null || s1.Trim().Length == 0)
{
- throw new ArgumentNullException("s1");
+ throw new ArgumentNullException(nameof(s1));
}
if (s1 == "/")
{
return null;
}
- var lastSeparatorIndex = s1.LastIndexOf("/", StringComparison.Ordinal);
+ var lastSeparatorIndex = s1.LastIndexOf('/');
// No directory
if (lastSeparatorIndex < 0)
{
@@ -543,7 +622,7 @@ static public bool IsRooted(string path, PathType pathType)
{
pathType = path.StartsWith("/") ? PathType.Linux : PathType.Windows;
}
- return pathType == PathType.Windows ? Path.IsPathRooted(path) : path.StartsWith("/");
+ return pathType == PathType.Windows ? IsWindowsPathRooted(path) : path.StartsWith("/");
}
}
}
diff --git a/src/Microsoft/SqlServer/Management/SqlEnum/XmlRead.CS b/src/Microsoft/SqlServer/Management/SqlEnum/XmlRead.CS
index 1abd0098..471c3016 100644
--- a/src/Microsoft/SqlServer/Management/SqlEnum/XmlRead.CS
+++ b/src/Microsoft/SqlServer/Management/SqlEnum/XmlRead.CS
@@ -432,19 +432,20 @@ namespace Microsoft.SqlServer.Management.Smo
string realPath = CodeGenSettings.Directory;
// Additional paths to look into. Pray that they have no duplicates!
+ // Built with Path.Combine so they resolve correctly on both Windows and Linux.
string[] subPaths = {
- @"..\..\..\SqlEnum\xml",
- @"..\..\..\SqlEnum\xml\endpoint",
- @"..\..\..\SqlEnum\xml\extendedproperty",
- @"..\..\..\SqlEnum\xml\include",
- @"..\..\..\SqlEnum\xml\perm",
- @"..\..\..\Smo.Broker\xml",
- @"..\..\..\SMO\Enumerator\sql\src\xml",
- @"..\..\..\SMO\Enumerator\sql\src\xml\endpoint",
- @"..\..\..\SMO\Enumerator\sql\src\xml\extendedproperty",
- @"..\..\..\SMO\Enumerator\sql\src\xml\include",
- @"..\..\..\SMO\Enumerator\sql\src\xml\perm",
- @"..\..\..\SMO\Enumerator\ServiceBroker\src\xml" };
+ Path.Combine("..", "..", "..", "SqlEnum", "xml"),
+ Path.Combine("..", "..", "..", "SqlEnum", "xml", "endpoint"),
+ Path.Combine("..", "..", "..", "SqlEnum", "xml", "extendedproperty"),
+ Path.Combine("..", "..", "..", "SqlEnum", "xml", "include"),
+ Path.Combine("..", "..", "..", "SqlEnum", "xml", "perm"),
+ Path.Combine("..", "..", "..", "Smo.Broker", "xml"),
+ Path.Combine("..", "..", "..", "SMO", "Enumerator", "sql", "src", "xml"),
+ Path.Combine("..", "..", "..", "SMO", "Enumerator", "sql", "src", "xml", "endpoint"),
+ Path.Combine("..", "..", "..", "SMO", "Enumerator", "sql", "src", "xml", "extendedproperty"),
+ Path.Combine("..", "..", "..", "SMO", "Enumerator", "sql", "src", "xml", "include"),
+ Path.Combine("..", "..", "..", "SMO", "Enumerator", "sql", "src", "xml", "perm"),
+ Path.Combine("..", "..", "..", "SMO", "Enumerator", "ServiceBroker", "src", "xml") };
string realFile = "";
foreach( string subPath in subPaths )
diff --git a/src/Microsoft/dirs.proj b/src/Microsoft/dirs.proj
index d50c2057..564457b5 100644
--- a/src/Microsoft/dirs.proj
+++ b/src/Microsoft/dirs.proj
@@ -4,5 +4,7 @@
+
+
\ No newline at end of file
diff --git a/src/UnitTest/Common/UnitTestBase.cs b/src/UnitTest/Common/UnitTestBase.cs
index b3244a4b..e29b5221 100644
--- a/src/UnitTest/Common/UnitTestBase.cs
+++ b/src/UnitTest/Common/UnitTestBase.cs
@@ -37,4 +37,16 @@ public virtual void MyTestCleanup()
}
}
+
+ public static class StringTestExtensions
+ {
+ ///
+ /// Converts \r\n in a string literal to Environment.NewLine, so string literals authored
+ /// with \r\n compare correctly against output that uses the host OS's native newline.
+ ///
+ public static string FixNewLines(this string input)
+ {
+ return Environment.NewLine == "\r\n" ? input : input.Replace("\r\n", Environment.NewLine);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/UnitTest/ConnectionInfo/ServerInformationTests.cs b/src/UnitTest/ConnectionInfo/ServerInformationTests.cs
index 5509a6fd..01d7bbde 100644
--- a/src/UnitTest/ConnectionInfo/ServerInformationTests.cs
+++ b/src/UnitTest/ConnectionInfo/ServerInformationTests.cs
@@ -33,22 +33,7 @@ public void GetServerInformation_executes_one_query_and_accepts_three_result_tab
var connectMock = new Mock();
var commandMock = new Mock();
var dataAdapterMock = new Mock();
- var expectedScript = @"DECLARE @edition sysname;
-SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname);
-SELECT case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType',
-@edition AS Edition,
-CONVERT(bigint, SERVERPROPERTY(N'EditionID')) AS EditionID,
-SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition,
-SERVERPROPERTY('ProductVersion') AS ProductVersion,
-@@MICROSOFTVERSION AS MicrosoftVersion,
-case when serverproperty('EngineEdition') = 12 then 1 when serverproperty('EngineEdition') = 11 and @@version like 'Microsoft Azure SQL Data Warehouse%' then 1 else 0 end as IsFabricServer,
-convert(sysname, SERVERPROPERTY(N'Collation')) AS Collation;
-select host_platform from sys.dm_os_host_info
-if @edition = N'SQL Azure'
- select 'TCP' as ConnectionProtocol
-else
- exec ('select CONVERT(nvarchar(40),CONNECTIONPROPERTY(''net_transport'')) as ConnectionProtocol')
-";
+ var expectedScript = "DECLARE @edition sysname;\r\nSET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname);\r\nSELECT case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType',\r\n@edition AS Edition,\r\nCONVERT(bigint, SERVERPROPERTY(N'EditionID')) AS EditionID,\r\nSERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition,\r\nSERVERPROPERTY('ProductVersion') AS ProductVersion,\r\n@@MICROSOFTVERSION AS MicrosoftVersion,\r\ncase when serverproperty('EngineEdition') = 12 then 1 when serverproperty('EngineEdition') = 11 and @@version like 'Microsoft Azure SQL Data Warehouse%' then 1 else 0 end as IsFabricServer,\r\nconvert(sysname, SERVERPROPERTY(N'Collation')) AS Collation;\r\nselect host_platform from sys.dm_os_host_info\r\nif @edition = N'SQL Azure' \r\n select 'TCP' as ConnectionProtocol\r\nelse\r\n exec ('select CONVERT(nvarchar(40),CONNECTIONPROPERTY(''net_transport'')) as ConnectionProtocol')\r\n";
// The Moq verification doesn't print a nice diff of the expected/actual,
// so let's use the nUnit assert for the string comparison
commandMock.SetupSet(c => c.CommandText = It.IsAny()).
@@ -89,22 +74,7 @@ public void When_ServerVersion_is_less_than_14_HostPlatform_is_hard_coded_Window
var commandMock = new Mock();
var dataAdapterMock = new Mock();
var expectedScript =
- @"DECLARE @edition sysname;
-SET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname);
-SELECT case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType',
-@edition AS Edition,
-CONVERT(bigint, SERVERPROPERTY(N'EditionID')) AS EditionID,
-SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition,
-SERVERPROPERTY('ProductVersion') AS ProductVersion,
-@@MICROSOFTVERSION AS MicrosoftVersion,
-case when serverproperty('EngineEdition') = 12 then 1 when serverproperty('EngineEdition') = 11 and @@version like 'Microsoft Azure SQL Data Warehouse%' then 1 else 0 end as IsFabricServer,
-convert(sysname, SERVERPROPERTY(N'Collation')) AS Collation;
-select N'Windows' as host_platform
-if @edition = N'SQL Azure'
- select 'TCP' as ConnectionProtocol
-else
- exec ('select CONVERT(nvarchar(40),CONNECTIONPROPERTY(''net_transport'')) as ConnectionProtocol')
-";
+ "DECLARE @edition sysname;\r\nSET @edition = cast(SERVERPROPERTY(N'EDITION') as sysname);\r\nSELECT case when @edition = N'SQL Azure' then 2 else 1 end as 'DatabaseEngineType',\r\n@edition AS Edition,\r\nCONVERT(bigint, SERVERPROPERTY(N'EditionID')) AS EditionID,\r\nSERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition,\r\nSERVERPROPERTY('ProductVersion') AS ProductVersion,\r\n@@MICROSOFTVERSION AS MicrosoftVersion,\r\ncase when serverproperty('EngineEdition') = 12 then 1 when serverproperty('EngineEdition') = 11 and @@version like 'Microsoft Azure SQL Data Warehouse%' then 1 else 0 end as IsFabricServer,\r\nconvert(sysname, SERVERPROPERTY(N'Collation')) AS Collation;\r\nselect N'Windows' as host_platform\r\nif @edition = N'SQL Azure' \r\n select 'TCP' as ConnectionProtocol\r\nelse\r\n exec ('select CONVERT(nvarchar(40),CONNECTIONPROPERTY(''net_transport'')) as ConnectionProtocol')\r\n";
// The Moq verification doesn't print a nice diff of the expected/actual,
// so let's use the nUnit assert for the string comparison
diff --git a/src/UnitTest/ConnectionInfo/SqlConnectionInfoTests.cs b/src/UnitTest/ConnectionInfo/SqlConnectionInfoTests.cs
index 27786359..12bc8957 100644
--- a/src/UnitTest/ConnectionInfo/SqlConnectionInfoTests.cs
+++ b/src/UnitTest/ConnectionInfo/SqlConnectionInfoTests.cs
@@ -11,6 +11,7 @@
using NUnit.Framework;
using System;
using System.ComponentModel;
+using System.Runtime.InteropServices;
using Assert = NUnit.Framework.Assert;
@@ -34,6 +35,11 @@ public void SqlConnectionInfoWithConnection_copy_returns_editable_ServerConnecti
[TestCategory("Unit")]
public void SqlConnectionInfo_impersonation_failure_throws_ConnectionFailureException()
{
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ // Assert.Ignore (NUnit) isn't understood by the MSTest adapter running this class and is reported as a failure, so use MSTest's own Inconclusive instead.
+ Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Inconclusive("ConnectAsUser impersonation relies on the Windows LogonUser API (advapi32.dll), which isn't supported on this platform.");
+ }
var conn = new ServerConnection("someservername") { ConnectAsUser = true, ConnectAsUserName = "someuser", ConnectAsUserPassword = Guid.NewGuid().ToString() };
var ex = Assert.Throws(conn.Connect, "Connect() should throw ConnectionFailureException when impersonation fails");
Assert.That(ex.InnerException, Is.InstanceOf(), "InnerException");
diff --git a/src/UnitTest/Directory.Build.targets b/src/UnitTest/Directory.Build.targets
index 8b3953b5..6bdd3524 100644
--- a/src/UnitTest/Directory.Build.targets
+++ b/src/UnitTest/Directory.Build.targets
@@ -5,25 +5,32 @@
- /Settings:$(EnlistmentRoot)\src\UnitTest\unittest.runsettings
- $(PkgMicrosoft_TestPlatform)\tools\net462\Common7\IDE\Extensions\TestPlatform\
+ /Settings:$(EnlistmentRoot)/src/UnitTest/unittest.runsettings
+ --settings "$(EnlistmentRoot)/src/UnitTest/unittest.runsettings"
+ $(PkgMicrosoft_TestPlatform)/tools/net462/Common7/IDE/Extensions/TestPlatform/
$(OutputPath)$(AssemblyName).dll
Unit
/TestCaseFilter:"TestCategory=$(TestCategory)"
+ --filter "TestCategory=$(TestCategory)"
-
-
+
+
+
+
+
+
+
-
+
diff --git a/src/UnitTest/Dmf/ExpressionTreeTests.cs b/src/UnitTest/Dmf/ExpressionTreeTests.cs
index 42c0a43a..7c380a1f 100644
--- a/src/UnitTest/Dmf/ExpressionTreeTests.cs
+++ b/src/UnitTest/Dmf/ExpressionTreeTests.cs
@@ -51,7 +51,7 @@ public void ExpressionNode_Parse()
var xml = SerializeExpressionNode(node);
Assert.That(node.ToString(), Is.EqualTo(testCase[0]), $"Should round trip expression [{testCase[0]}]");
Assert.That(node.ToStringForDisplay(), Is.EqualTo(testCase[1]), $"ToStringForDisplay of [{testCase[0]}]");
- Assert.That(xml, Is.EqualTo(testCase[2]), $"SerializeExpressionNode for [{testCase[0]}]");
+ Assert.That(xml, Is.EqualTo(testCase[2].FixNewLines()), $"SerializeExpressionNode for [{testCase[0]}]");
}
});
}
diff --git a/src/UnitTest/RegisteredServers/RegisteredServersTests.cs b/src/UnitTest/RegisteredServers/RegisteredServersTests.cs
index 6dbd23b7..b70ca267 100644
--- a/src/UnitTest/RegisteredServers/RegisteredServersTests.cs
+++ b/src/UnitTest/RegisteredServers/RegisteredServersTests.cs
@@ -8,6 +8,7 @@
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Runtime.InteropServices;
using Assert = NUnit.Framework.Assert;
namespace Microsoft.SqlServer.Test.RegisteredServersUnitTests
@@ -75,6 +76,11 @@ public void RegisteredServersStore_initializes_from_valid_file()
[TestCategory("Unit")]
public void ServerGroup_Alter_saves_file_with_added_server()
{
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ // Assert.Ignore (NUnit) isn't understood by the MSTest adapter running this class and is reported as a failure, so use MSTest's own Inconclusive instead.
+ Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Inconclusive("Encrypted credential persistence relies on Windows DPAPI (ProtectedData), which isn't supported on this platform.");
+ }
var tempFile = Path.GetTempFileName();
File.Delete(tempFile);
try
diff --git a/src/UnitTest/Sfc/Microsoft.SqlServer.Test.SfcUnitTests.csproj b/src/UnitTest/Sfc/Microsoft.SqlServer.Test.SfcUnitTests.csproj
index 9254a0d6..52cb05c4 100644
--- a/src/UnitTest/Sfc/Microsoft.SqlServer.Test.SfcUnitTests.csproj
+++ b/src/UnitTest/Sfc/Microsoft.SqlServer.Test.SfcUnitTests.csproj
@@ -8,5 +8,6 @@
+
\ No newline at end of file
diff --git a/src/UnitTest/Smo/PathWrapperTests.cs b/src/UnitTest/Smo/PathWrapperTests.cs
index c62f6f16..55f25e3d 100644
--- a/src/UnitTest/Smo/PathWrapperTests.cs
+++ b/src/UnitTest/Smo/PathWrapperTests.cs
@@ -26,6 +26,7 @@ public void When_PathType_is_Windows_Combine_mimics_PathdotCombine()
Assert.That(PathWrapper.Combine(@"c:^*&)(_=@#'\^.*(.txt", @"subdir\file.txt"),
Is.EqualTo(@"c:^*&)(_=@#'\^.*(.txt\subdir\file.txt"), "unsupported characters");
Assert.That(PathWrapper.Combine(@"", @"subdir\file.txt"), Is.EqualTo(@"subdir\file.txt"), "path1 empty");
+ Assert.That(PathWrapper.Combine(@"c:\temp", ""), Is.EqualTo(@"c:\temp"), "path2 empty");
Assert.That(PathWrapper.Combine(@"\\server\share\", "folder"), Is.EqualTo(@"\\server\share\folder"),
"unc path");
Assert.That(
@@ -75,6 +76,8 @@ public void When_PathType_is_Windows_GetDirectoryName_mimics_PathdotGetDirectory
Assert.That(PathWrapper.GetDirectoryName(@"C:\MyDir"), Is.EqualTo(@"C:\"), "folder in root");
Assert.That(PathWrapper.GetDirectoryName(@"C:\"), Is.Null, "root");
Assert.That(PathWrapper.GetDirectoryName(@"filename", PathType.Windows), Is.Empty, "no folder");
+ Assert.That(PathWrapper.GetDirectoryName(@"\\server\share"), Is.Null, "unc share root");
+ Assert.That(PathWrapper.GetDirectoryName(@"\\server\share\"), Is.EqualTo(@"\\server\share"), "unc share root with trailing separator");
Assert.That(PathWrapper.GetDirectoryName(@"\\server\share\folder"), Is.EqualTo(@"\\server\share"), "unc path");
}
diff --git a/src/UnitTest/dirs.proj b/src/UnitTest/dirs.proj
index 2fa73a2b..e9df8845 100644
--- a/src/UnitTest/dirs.proj
+++ b/src/UnitTest/dirs.proj
@@ -2,5 +2,7 @@
+
+
\ No newline at end of file