mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-07 17:25:04 -05:00
Enhanced Logging for sqltoolsservice (#695)
This change modifies the logging framework within sqltoolservice. Moves away from custom Logger object to start using .Net tracing framework. It supports for the static Trace and TraceSource way of logging. For all new code it is recommend that we log the log messages using the existing static Logger class, while the code changes will continue to route the older Trace.Write* calls from the process to same log listeners (and thus the log targets) as used by the Logger class. Thus tracing in SMO code that uses Trace.Write* methods gets routed to the same file as the messages from rest of SQLTools Service code. Make changes to start using .Net Frameworks codebase for all logging to unify our logging story. Allows parameter to set tracingLevel filters that controls what kinds of message make it to the log file. Allows a parameter to set a specific log file name so if these gets set by external code (the UI code using the tools service for example) then the external code is aware of the current log file in use. Adding unittests to test out the existing and improved logging capabilities. Sequences of checkins in development branch: * Saving v1 of logging to prepare for code review. Minor cleanup and some end to end testing still remains * Removing local launchSettings.json files * added support for lazy listener to sqltoolsloglistener and removed incorrect changes to comments across files in previous checkin * Converting time to local time when writing entries to the log * move the hosting.v2 to new .net based logging code * removing *.dgml files and addding them to .gitignore * fixing typo of defaultTraceSource * Addressing pull request feedback * Adding a test to verify logging from SMO codebase * propogating changes to v1 sqltools.hosting commandoptions.cs to the v2 version * Fixing comments on start and stop callstack methods and whitespaces * Commenting a test that got uncommented by mistake * addding .gitattributes file as .sql file was observed to be misconstrued as a binary file
This commit is contained in:
@@ -12,6 +12,6 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.SqlTools.DataProtocol.Contracts\Microsoft.SqlTools.DataProtocol.Contracts.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.SqlTools.Hosting.Contracts\Microsoft.SqlTools.Hosting.Contracts.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.SqlTools.Hosting.v2\Microsoft.SqlTools.Hosting.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.SqlTools.Hosting.v2\Microsoft.SqlTools.Hosting.v2.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -3,6 +3,7 @@
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlTools.Hosting.Utility;
|
||||
@@ -30,28 +31,28 @@ namespace Microsoft.SqlTools.Hosting.UnitTests.UtilityTests
|
||||
.ToList()
|
||||
.ForEach(File.Delete);
|
||||
|
||||
Logger logger = new Logger();
|
||||
// initialize the logger
|
||||
logger.Initialize(
|
||||
Logger.Initialize(
|
||||
logFilePath: Path.Combine(Directory.GetCurrentDirectory(), "sqltools"),
|
||||
minimumLogLevel: LogLevel.Verbose);
|
||||
tracingLevel: SourceLevels.Verbose);
|
||||
|
||||
// Write a test message.
|
||||
string logMessage = $"Message from {nameof(LoggerDefaultFile)} test";
|
||||
Logger.Write(TraceEventType.Information, logMessage);
|
||||
|
||||
// close the logger
|
||||
logger.Close();
|
||||
Logger.Close();
|
||||
|
||||
// find the name of the new log file
|
||||
string logFileName = Directory.GetFiles(Directory.GetCurrentDirectory())
|
||||
.SingleOrDefault(fileName =>
|
||||
fileName.Contains("sqltools_")
|
||||
&& fileName.EndsWith(".log", StringComparison.OrdinalIgnoreCase));
|
||||
string logFileName = Logger.LogFileFullPath;
|
||||
|
||||
// validate the log file was created with desired name
|
||||
Assert.True(!string.IsNullOrWhiteSpace(logFileName));
|
||||
if (!string.IsNullOrWhiteSpace(logFileName))
|
||||
{
|
||||
Assert.True(logFileName.Length > "sqltools_.log".Length);
|
||||
Assert.True(File.Exists(logFileName));
|
||||
|
||||
Assert.True(File.Exists(logFileName), $"the log file: {logFileName} must exist");
|
||||
//Ensure that our log message exists in the log file
|
||||
Assert.True(File.ReadAllText(logFileName).Contains(logMessage, StringComparison.InvariantCultureIgnoreCase), $"the log message:'{logMessage}' must be present in the log file");
|
||||
// delete the test log file
|
||||
if (File.Exists(logFileName))
|
||||
{
|
||||
|
||||
@@ -105,6 +105,24 @@ GO";
|
||||
Cleanup(locations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoggerGetValidTableDefinitionTest()
|
||||
{
|
||||
TestLogger test = new TestLogger()
|
||||
{
|
||||
TraceSource = System.Reflection.MethodInfo.GetCurrentMethod().Name,
|
||||
EventType = System.Diagnostics.TraceEventType.Information,
|
||||
TracingLevel = System.Diagnostics.SourceLevels.All,
|
||||
};
|
||||
|
||||
test.Initialize();
|
||||
GetValidTableDefinitionTest(); // This should emit log.from SMO code base
|
||||
test.LogMessage = "OnScriptingProgress ScriptingCompleted"; //Log message to verify. This message comes from SMO code.
|
||||
test.Verify(); // The log message should be absent since the tracing level is set to Off.
|
||||
test.Cleanup();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test get definition for a invalid table object with active connection
|
||||
/// </summary>
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="../../Common.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<DebugType>portable</DebugType>
|
||||
<AssemblyName>Microsoft.SqlTools.ServiceLayer.IntegrationTests</AssemblyName>
|
||||
<PackageId>Microsoft.SqlTools.ServiceLayer.IntegrationTests</PackageId>
|
||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
||||
<DefineConstants>$(DefineConstants);TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>../../bin/ref/Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq">
|
||||
<HintPath>../../bin/ref/Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Castle.Core">
|
||||
<HintPath>../../bin/ref/Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>../../bin/ref/Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq">
|
||||
<HintPath>../../bin/ref/Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Castle.Core">
|
||||
<HintPath>../../bin/ref/Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<ProjectReference Include="../../src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj" />
|
||||
<ProjectReference Include="../../src/Microsoft.SqlTools.Hosting/Microsoft.SqlTools.Hosting.csproj" />
|
||||
<ProjectReference Include="../../src/Microsoft.SqlTools.Credentials/Microsoft.SqlTools.Credentials.csproj" />
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.SqlTools.ServiceLayer.TestDriver.Driver;
|
||||
using Microsoft.SqlTools.ServiceLayer.TestDriver.Utility;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
@@ -24,7 +25,7 @@ namespace Microsoft.SqlTools.ServiceLayer.PerfTests
|
||||
return 0;
|
||||
}
|
||||
|
||||
Logger.Initialize("testdriver", LogLevel.Verbose);
|
||||
Logger.Initialize(logFilePath: Logger.GenerateLogFilePath("testdriver"), traceSource: "perftests", tracingLevel: SourceLevels.Verbose);
|
||||
return TestRunner.Instance.RunTests(args, "Microsoft.SqlTools.ServiceLayer.PerfTests.").Result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
|
||||
<EnableDefaultNoneItems>false</EnableDefaultNoneItems>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<DefineConstants>$(DefineConstants);NETCOREAPP1_0</DefineConstants>
|
||||
<DefineConstants>$(DefineConstants);NETCOREAPP1_0;TRACE</DefineConstants>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
|
||||
168
test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestLogger.cs
Normal file
168
test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestLogger.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
{
|
||||
public class TestLogger
|
||||
{
|
||||
private string logFilePath;
|
||||
private string logMessage;
|
||||
private string logContents;
|
||||
private string logFileName;
|
||||
private string topFrame;
|
||||
|
||||
public bool ShouldVerifyCallstack { get; set; } = false;
|
||||
public string TestName { get => testName ?? TraceSource; set => testName = value; }
|
||||
public string TraceSource { get; set; } = "sqltoolsTest";
|
||||
public string LogMessage { get => logMessage ?? $"{TestName} test message"; set => logMessage = value; }
|
||||
public string LogFilePath { get => logFilePath ?? Logger.GenerateLogFilePath(Path.Combine(Directory.GetCurrentDirectory(), TraceSource)); set => logFilePath = value; }
|
||||
public TraceEventType EventType { get; set; } = TraceEventType.Information;
|
||||
public SourceLevels TracingLevel { get; set; } = SourceLevels.Critical;
|
||||
public bool DoNotUseTraceSource { get; set; } = false;
|
||||
|
||||
private List<Action> pendingVerifications;
|
||||
private string testName;
|
||||
|
||||
public string CallstackMessage { get => $"Callstack=\\s*{TopFrame}"; }
|
||||
|
||||
public string LogFileName { get => logFileName ?? Logger.LogFileFullPath; set => logFileName = value; }
|
||||
public void Initialize() =>
|
||||
Logger.Initialize(TracingLevel, LogFilePath, TraceSource); // initialize the logger
|
||||
public string LogContents
|
||||
{
|
||||
get
|
||||
{
|
||||
if (logContents == null)
|
||||
{
|
||||
Logger.Close();
|
||||
Assert.True(!string.IsNullOrWhiteSpace(LogFileName));
|
||||
Assert.True(LogFileName.Length > "{TraceSource}_.log".Length);
|
||||
Assert.True(File.Exists(LogFileName));
|
||||
logContents = File.ReadAllText(LogFileName);
|
||||
}
|
||||
return logContents;
|
||||
}
|
||||
set => logContents = value;
|
||||
}
|
||||
|
||||
public string TopFrame { get => topFrame ?? "at System.Environment.get_StackTrace()"; set => topFrame = value; }
|
||||
|
||||
public List<Action> PendingVerifications
|
||||
{
|
||||
get
|
||||
{
|
||||
if (pendingVerifications == null)
|
||||
{
|
||||
pendingVerifications = new List<Action>();
|
||||
}
|
||||
return pendingVerifications;
|
||||
}
|
||||
set => pendingVerifications = value;
|
||||
}
|
||||
|
||||
public void Write()
|
||||
{
|
||||
// write test log
|
||||
if (DoNotUseTraceSource)
|
||||
{
|
||||
TraceSource savedTraceSource = Logger.TraceSource;
|
||||
Logger.TraceSource = null;
|
||||
Logger.Write(EventType, LogMessage);
|
||||
Logger.TraceSource = savedTraceSource;
|
||||
}
|
||||
else
|
||||
Logger.Write(EventType, LogMessage);
|
||||
}
|
||||
|
||||
public void WriteWithCallstack()
|
||||
{
|
||||
// write test log with callstack
|
||||
Logger.WriteWithCallstack(EventType, LogMessage);
|
||||
ShouldVerifyCallstack = true;
|
||||
}
|
||||
|
||||
public void Verify(bool expectLogMessage = true) => Verify(ShouldVerifyCallstack, expectLogMessage);
|
||||
|
||||
public void Verify(bool shouldVerifyCallstack, bool expectLogMessage = true) => Verify(EventType, LogMessage, CallstackMessage, shouldVerifyCallstack, expectLogMessage);
|
||||
|
||||
public void Verify(TraceEventType eventType, string message, string callstackMessage, bool shouldVerifyCallstack = false, bool expectLogMessage = true)
|
||||
{
|
||||
if (expectLogMessage)
|
||||
{
|
||||
Assert.True(File.Exists(Logger.LogFileFullPath) && Regex.IsMatch(LogContents, $@"\b{eventType}:\s+\d+\s+:\s+{message}", RegexOptions.Compiled));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.False(File.Exists(Logger.LogFileFullPath) && Regex.IsMatch(LogContents, $@"\b{eventType}:\s+\d+\s+:\s+{message}", RegexOptions.Compiled));
|
||||
}
|
||||
if (shouldVerifyCallstack)
|
||||
{
|
||||
VerifyCallstack(callstackMessage, expectLogMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform all the pending verifications
|
||||
/// </summary>
|
||||
public void VerifyPending()
|
||||
{
|
||||
foreach (var pv in PendingVerifications)
|
||||
{
|
||||
pv.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public void VerifyCallstack(bool expectLogMessage = true) => VerifyCallstack(CallstackMessage, expectLogMessage);
|
||||
|
||||
public void VerifyCallstack(string message, bool expectLogMessage = true)
|
||||
{
|
||||
if (expectLogMessage)
|
||||
{
|
||||
Assert.True(File.Exists(Logger.LogFileFullPath) && Regex.IsMatch(LogContents, $"{message}", RegexOptions.Compiled));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.False(File.Exists(Logger.LogFileFullPath) && Regex.IsMatch(LogContents, $"{message}", RegexOptions.Compiled));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void VerifyInitialization(SourceLevels expectedTracingLevel, string expectedTraceSource, string logFilePath, bool isLogFileExpectedToExist, int? testNo = null)
|
||||
{
|
||||
string FailureMessagePrefix = testNo.HasValue ? $"For Test No:{testNo.Value.ToString()}," : string.Empty;
|
||||
Assert.False(string.IsNullOrWhiteSpace(logFilePath), $"{FailureMessagePrefix} LogFilePath should not be null or whitespaces");
|
||||
Assert.True(expectedTracingLevel == Logger.TracingLevel, $"{FailureMessagePrefix} expected Tracing Level did not match the configured value");
|
||||
if (isLogFileExpectedToExist)
|
||||
{
|
||||
Assert.True(File.Exists(logFilePath), $"{FailureMessagePrefix} logFilePath:{logFilePath} must exist");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.False(File.Exists(logFilePath), $"{FailureMessagePrefix} {logFilePath} must not exist");
|
||||
}
|
||||
Assert.True(string.Compare(expectedTraceSource, Logger.TraceSource.Name, ignoreCase: true) == 0, $"{FailureMessagePrefix} expected Trace Source Name did not match the configured value");
|
||||
}
|
||||
|
||||
public void Cleanup() => Cleanup(Logger.LogFileFullPath);
|
||||
|
||||
public static void Cleanup(string logFileName)
|
||||
{
|
||||
if (File.Exists(logFileName))
|
||||
{
|
||||
Logger.Close();
|
||||
File.Delete(logFileName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Tests
|
||||
return 0;
|
||||
}
|
||||
|
||||
Logger.Initialize("testdriver", LogLevel.Verbose);
|
||||
Logger.Initialize("testdriver", TraceEventType.Verbose);
|
||||
|
||||
int returnCode = 0;
|
||||
Task.Run(async () =>
|
||||
|
||||
@@ -12,8 +12,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Connection
|
||||
{
|
||||
public class DatabaseLocksManagerTests
|
||||
{
|
||||
private string server1 = "server1";
|
||||
private string database1 = "database1";
|
||||
private const string server1 = "server1";
|
||||
private const string database1 = "database1";
|
||||
|
||||
[Fact]
|
||||
public void GainFullAccessShouldDisconnectTheConnections()
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
|
||||
// capture listener event notifications
|
||||
// capture Listener event notifications
|
||||
var mockListener = new Mock<IProfilerSessionListener>();
|
||||
mockListener.Setup(p => p.EventsAvailable(It.IsAny<string>(), It.IsAny<List<ProfilerEvent>>(), It.IsAny<bool>())).Callback(() =>
|
||||
{
|
||||
@@ -163,7 +163,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
|
||||
return Task.FromResult(0);
|
||||
});
|
||||
|
||||
// capture listener event notifications
|
||||
// capture Listener event notifications
|
||||
var mockListener = new Mock<IProfilerSessionListener>();
|
||||
mockListener.Setup(p => p.EventsAvailable(It.IsAny<string>(), It.IsAny<List<ProfilerEvent>>(), It.IsAny<bool>())).Callback(() =>
|
||||
{
|
||||
@@ -212,7 +212,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
|
||||
|
||||
profilerService.SessionMonitor.PollSession(1);
|
||||
|
||||
// confirm that no events were sent to paused listener
|
||||
// confirm that no events were sent to paused Listener
|
||||
Assert.False(recievedEvents);
|
||||
|
||||
// unpause viewer
|
||||
@@ -229,7 +229,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
|
||||
// check that events got sent to listener
|
||||
// check that events got sent to Listener
|
||||
Assert.True(recievedEvents);
|
||||
|
||||
requestContext.VerifyAll();
|
||||
@@ -244,7 +244,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Profiler
|
||||
bool sessionStopped = false;
|
||||
string testUri = "profiler_uri";
|
||||
|
||||
// capture listener event notifications
|
||||
// capture Listener event notifications
|
||||
var mockSession = new Mock<IXEventSession>();
|
||||
mockSession.Setup(p => p.GetTargetXml()).Callback(() =>
|
||||
{
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using Xunit;
|
||||
|
||||
@@ -15,48 +16,447 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ServiceHost
|
||||
/// </summary>
|
||||
public class LoggerTests
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Test to verify that the logger initialization is generating a valid file
|
||||
/// Verifies that a test log entries is succesfully written to a default log file.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LoggerDefaultFile()
|
||||
{
|
||||
// delete any existing log files from the current directory
|
||||
Directory.GetFiles(Directory.GetCurrentDirectory())
|
||||
.Where(fileName
|
||||
=> fileName.Contains("sqltools_")
|
||||
&& fileName.EndsWith(".log", StringComparison.OrdinalIgnoreCase))
|
||||
.ToList()
|
||||
.ForEach(File.Delete);
|
||||
|
||||
// initialize the logger
|
||||
Logger.Initialize(
|
||||
logFilePath: Path.Combine(Directory.GetCurrentDirectory(), "sqltools"),
|
||||
minimumLogLevel: LogLevel.Verbose);
|
||||
|
||||
// close the logger
|
||||
Logger.Close();
|
||||
|
||||
// find the name of the new log file
|
||||
string logFileName = Directory.GetFiles(Directory.GetCurrentDirectory())
|
||||
.SingleOrDefault(fileName =>
|
||||
fileName.Contains("sqltools_")
|
||||
&& fileName.EndsWith(".log", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// validate the log file was created with desired name
|
||||
Assert.True(!string.IsNullOrWhiteSpace(logFileName));
|
||||
if (!string.IsNullOrWhiteSpace(logFileName))
|
||||
TestLogger test = new TestLogger()
|
||||
{
|
||||
Assert.True(logFileName.Length > "sqltools_.log".Length);
|
||||
Assert.True(File.Exists(logFileName));
|
||||
TraceSource = MethodInfo.GetCurrentMethod().Name,
|
||||
EventType = TraceEventType.Information,
|
||||
TracingLevel = SourceLevels.Verbose,
|
||||
};
|
||||
|
||||
// delete the test log file
|
||||
if (File.Exists(logFileName))
|
||||
{
|
||||
File.Delete(logFileName);
|
||||
}
|
||||
test.Initialize();
|
||||
test.Write();
|
||||
test.Verify();
|
||||
test.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to verify that the logger initialization works using various possibilities.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LoggerTestInitialization()
|
||||
{
|
||||
int? testNo = 1;
|
||||
//Test 1: Initialization with all defaults. Logfile names get autogenerated with the well known prefix.
|
||||
{
|
||||
SourceLevels expectedTracingLevel = Logger.defaultTracingLevel;
|
||||
string expectedTraceSource = Logger.defaultTraceSource;
|
||||
Logger.Initialize();
|
||||
bool isLogFileExpectedToExist = ((uint)expectedTracingLevel >= (uint)SourceLevels.Information);
|
||||
TestLogger.VerifyInitialization(expectedTracingLevel, expectedTraceSource, Logger.LogFileFullPath, isLogFileExpectedToExist, testNo++);
|
||||
TestLogger.Cleanup(Logger.LogFileFullPath);
|
||||
}
|
||||
|
||||
//Test 2: Initialization with TracingLevel set to Critical. Logfile names get autogenerated with the well known prefix. Before we do a write at Critical level the logfile must not get even created.
|
||||
{
|
||||
SourceLevels expectedTracingLevel = SourceLevels.Critical;
|
||||
string expectedTraceSource = Logger.defaultTraceSource;
|
||||
Logger.Initialize(expectedTracingLevel);
|
||||
bool isLogFileExpectedToExist = ((uint)expectedTracingLevel >= (uint)SourceLevels.Information);
|
||||
TestLogger.VerifyInitialization(expectedTracingLevel, expectedTraceSource, Logger.LogFileFullPath, isLogFileExpectedToExist, testNo++);
|
||||
TestLogger.Cleanup(Logger.LogFileFullPath);
|
||||
}
|
||||
|
||||
//Test 3: Initialization with TraceSourceName set to specified name. Logfile names get autogenerated with the well known prefix.
|
||||
{
|
||||
SourceLevels expectedTracingLevel = Logger.defaultTracingLevel;
|
||||
string expectedTraceSource = nameof(LoggerTestInitialization);
|
||||
Logger.Initialize(traceSource:expectedTraceSource);
|
||||
bool isLogFileExpectedToExist = ((uint)expectedTracingLevel >= (uint)SourceLevels.Information);
|
||||
TestLogger.VerifyInitialization(expectedTracingLevel, expectedTraceSource, Logger.LogFileFullPath, isLogFileExpectedToExist, testNo++);
|
||||
TestLogger.Cleanup(Logger.LogFileFullPath);
|
||||
}
|
||||
|
||||
|
||||
//Test 4: Initialization with logfile set to specified random filepath.
|
||||
{
|
||||
SourceLevels expectedTracingLevel = Logger.defaultTracingLevel;
|
||||
string expectedTraceSource = Logger.defaultTraceSource;
|
||||
string logFilePath = Path.Combine(Path.GetRandomFileName(), nameof(LoggerTestInitialization));
|
||||
Logger.Initialize(traceSource: expectedTraceSource, logFilePath: logFilePath);
|
||||
Assert.True(string.Compare(logFilePath, Logger.LogFileFullPath, ignoreCase: true) == 0, "The logfile path of the Logger should be the one specified");
|
||||
bool isLogFileExpectedToExist = ((uint)expectedTracingLevel >= (uint)SourceLevels.Information);
|
||||
TestLogger.VerifyInitialization(expectedTracingLevel, expectedTraceSource, logFilePath, isLogFileExpectedToExist, testNo++);
|
||||
TestLogger.Cleanup(Logger.LogFileFullPath);
|
||||
}
|
||||
|
||||
//Test 5: Initialization with logfile generated from log directory and LogFilePrefix using Logger.GenerateLogFielPath method.
|
||||
{
|
||||
SourceLevels expectedTracingLevel = Logger.defaultTracingLevel;
|
||||
string expectedTraceSource = Logger.defaultTraceSource;
|
||||
string logFilePath = Logger.GenerateLogFilePath(Path.Combine(Directory.GetCurrentDirectory(), nameof(LoggerTestInitialization)));
|
||||
Assert.True(string.Compare(Path.GetDirectoryName(logFilePath), Directory.GetCurrentDirectory(), ignoreCase: true) == 0, "The directory path of the logfile should match the directory path specified");
|
||||
Logger.Initialize(traceSource: expectedTraceSource, logFilePath: logFilePath);
|
||||
Assert.True(string.Compare(logFilePath, Logger.LogFileFullPath, ignoreCase: true) == 0, "The logfile path of the Logger should be the one specified");
|
||||
bool isLogFileExpectedToExist = ((uint)expectedTracingLevel >= (uint)SourceLevels.Information);
|
||||
TestLogger.VerifyInitialization(expectedTracingLevel, expectedTraceSource, Logger.LogFileFullPath, isLogFileExpectedToExist, testNo++);
|
||||
TestLogger.Cleanup(Logger.LogFileFullPath);
|
||||
}
|
||||
|
||||
#region TracingLevel Settings
|
||||
//Test 6: Initialization tracingLevel specified as a null string.
|
||||
{
|
||||
string tracingLevel = null;
|
||||
SourceLevels expectedTracingLevel = Logger.defaultTracingLevel;
|
||||
string expectedTraceSource = Logger.defaultTraceSource;
|
||||
Logger.Initialize(tracingLevel);
|
||||
bool isLogFileExpectedToExist = false;
|
||||
TestLogger.VerifyInitialization(expectedTracingLevel, expectedTraceSource, Logger.LogFileFullPath, isLogFileExpectedToExist, testNo++);
|
||||
TestLogger.Cleanup(Logger.LogFileFullPath);
|
||||
}
|
||||
|
||||
//Test 7: Initialization tracingLevel specified as an empty string.
|
||||
{
|
||||
string tracingLevel = null;
|
||||
SourceLevels expectedTracingLevel = Logger.defaultTracingLevel;
|
||||
string expectedTraceSource = Logger.defaultTraceSource;
|
||||
Logger.Initialize(tracingLevel);
|
||||
bool isLogFileExpectedToExist = false;
|
||||
TestLogger.VerifyInitialization(expectedTracingLevel, expectedTraceSource, Logger.LogFileFullPath, isLogFileExpectedToExist, testNo++);
|
||||
TestLogger.Cleanup(Logger.LogFileFullPath);
|
||||
}
|
||||
|
||||
//Test 8: Initialization tracingLevel specified as an invalid string.
|
||||
{
|
||||
string tracingLevel = "invalid";
|
||||
SourceLevels expectedTracingLevel = Logger.defaultTracingLevel;
|
||||
string expectedTraceSource = Logger.defaultTraceSource;
|
||||
Logger.Initialize(tracingLevel);
|
||||
bool isLogFileExpectedToExist = false;
|
||||
TestLogger.VerifyInitialization(expectedTracingLevel, expectedTraceSource, Logger.LogFileFullPath, isLogFileExpectedToExist, testNo++);
|
||||
TestLogger.Cleanup(Logger.LogFileFullPath);
|
||||
}
|
||||
|
||||
//Test 9: Initialization with logfile set to empty string.
|
||||
{
|
||||
SourceLevels expectedTracingLevel = SourceLevels.All;
|
||||
string expectedTraceSource = Logger.defaultTraceSource;
|
||||
string logFilePath = string.Empty;
|
||||
Logger.Initialize(traceSource: expectedTraceSource, logFilePath: logFilePath, tracingLevel:expectedTracingLevel);
|
||||
bool isLogFileExpectedToExist = ((uint)expectedTracingLevel >= (uint)SourceLevels.Information);
|
||||
TestLogger.VerifyInitialization(expectedTracingLevel, expectedTraceSource, Logger.LogFileFullPath, isLogFileExpectedToExist, testNo++);
|
||||
TestLogger.Cleanup(Logger.LogFileFullPath);
|
||||
}
|
||||
//Test 10: Initialization with logfile set to null.
|
||||
{
|
||||
SourceLevels expectedTracingLevel = SourceLevels.All;
|
||||
string expectedTraceSource = Logger.defaultTraceSource;
|
||||
string logFilePath = null;
|
||||
Logger.Initialize(traceSource: expectedTraceSource, logFilePath: logFilePath, tracingLevel: expectedTracingLevel);
|
||||
bool isLogFileExpectedToExist = ((uint)expectedTracingLevel >= (uint)SourceLevels.Information);
|
||||
TestLogger.VerifyInitialization(expectedTracingLevel, expectedTraceSource, Logger.LogFileFullPath, isLogFileExpectedToExist, testNo++);
|
||||
TestLogger.Cleanup(Logger.LogFileFullPath);
|
||||
}
|
||||
//Test 11: Initialization with LogDirectory in Logger.GenerateLogFilePath set to empty string.
|
||||
{
|
||||
SourceLevels expectedTracingLevel = SourceLevels.All;
|
||||
string expectedTraceSource = Logger.defaultTraceSource;
|
||||
string logFilePath = Logger.GenerateLogFilePath(Path.Combine(string.Empty, nameof(LoggerTestInitialization)));
|
||||
Logger.Initialize(traceSource: expectedTraceSource, logFilePath: logFilePath, tracingLevel: expectedTracingLevel);
|
||||
Assert.True(string.Compare(logFilePath, Logger.LogFileFullPath, ignoreCase: true) == 0, "The logfile should match the path specified");
|
||||
bool isLogFileExpectedToExist = ((uint)expectedTracingLevel >= (uint)SourceLevels.Information);
|
||||
TestLogger.VerifyInitialization(expectedTracingLevel, expectedTraceSource, Logger.LogFileFullPath, isLogFileExpectedToExist, testNo++);
|
||||
TestLogger.Cleanup(Logger.LogFileFullPath);
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to verify that there is no log file created if TracingLevel is set to off.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LoggerTracingLevelOff()
|
||||
{
|
||||
TestLogger test = new TestLogger()
|
||||
{
|
||||
TraceSource = MethodInfo.GetCurrentMethod().Name,
|
||||
EventType = TraceEventType.Information,
|
||||
TracingLevel = SourceLevels.Off,
|
||||
};
|
||||
|
||||
test.Initialize();
|
||||
test.Write();
|
||||
Assert.False(File.Exists(Logger.LogFileFullPath), $"Log file must not exist when tracing level is: {test.TracingLevel}");
|
||||
test.Verify(expectLogMessage: false); // The log message should be absent since the tracing level is set to Off.
|
||||
test.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to verify that the tracinglevel setting filters message logged at lower levels.
|
||||
/// Verifies that a test log entries logged at Information level are not present in log when tracingLevel
|
||||
/// is set to 'Critical'
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LoggerInformationalNotLoggedWithCriticalTracingLevel()
|
||||
{
|
||||
TestLogger test = new TestLogger()
|
||||
{
|
||||
TraceSource = MethodInfo.GetCurrentMethod().Name,
|
||||
EventType = TraceEventType.Information,
|
||||
TracingLevel = SourceLevels.Critical,
|
||||
};
|
||||
|
||||
test.Initialize();
|
||||
test.Write();
|
||||
test.Verify(expectLogMessage:false); // The log message should be absent since the tracing level is set to collect messages only at 'Critical' logging level
|
||||
test.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to verify that WriteWithCallstack() method turns on the callstack logging
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LoggerWithCallstack()
|
||||
{
|
||||
TestLogger test = new TestLogger()
|
||||
{
|
||||
TraceSource = MethodInfo.GetCurrentMethod().Name,
|
||||
EventType = TraceEventType.Warning,
|
||||
TracingLevel = SourceLevels.Information,
|
||||
};
|
||||
|
||||
test.Initialize();
|
||||
test.WriteWithCallstack();
|
||||
test.Verify(); // This should verify the logging of callstack fields as well.
|
||||
test.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to verify that callstack logging is turned on, it does not get logged because tracing level filters them out.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LoggerWithCallstackFilteredOut()
|
||||
{
|
||||
TestLogger test = new TestLogger()
|
||||
{
|
||||
TraceSource = MethodInfo.GetCurrentMethod().Name,
|
||||
EventType = TraceEventType.Information,
|
||||
TracingLevel = SourceLevels.Error,
|
||||
};
|
||||
|
||||
test.Initialize();
|
||||
test.WriteWithCallstack();
|
||||
test.Verify(expectLogMessage:false); // The log message and corresponding callstack details should be absent since the tracing level is set to collect messages only at 'Error' logging level
|
||||
test.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// No TraceSource test to verify that WriteWithCallstack() method turns on the callstack logging
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LoggerNoTraceSourceWithCallstack()
|
||||
{
|
||||
TestLogger test = new TestLogger()
|
||||
{
|
||||
TraceSource = MethodInfo.GetCurrentMethod().Name,
|
||||
EventType = TraceEventType.Warning,
|
||||
TracingLevel = SourceLevels.Information,
|
||||
DoNotUseTraceSource = true,
|
||||
};
|
||||
|
||||
test.Initialize();
|
||||
test.WriteWithCallstack();
|
||||
test.Verify(); // This should verify the logging of callstack fields as well.
|
||||
test.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// No TraceSrouce test to verify that callstack logging is turned on, it does not get logged because tracing level filters them out.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LoggerNoTraceSourceWithCallstackFilteredOut()
|
||||
{
|
||||
TestLogger test = new TestLogger()
|
||||
{
|
||||
TraceSource = MethodInfo.GetCurrentMethod().Name,
|
||||
EventType = TraceEventType.Information,
|
||||
TracingLevel = SourceLevels.Error,
|
||||
DoNotUseTraceSource = true,
|
||||
};
|
||||
|
||||
test.Initialize();
|
||||
test.WriteWithCallstack();
|
||||
test.Verify(expectLogMessage: false); // The log message and corresponding callstack details should be absent since the tracing level is set to collect messages only at 'Error' logging level
|
||||
test.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests to verify that upon changing TracingLevel from Warning To Error,
|
||||
/// after the change, messages of Error type are present in the log and those logged with warning type are not present.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LoggerTracingLevelFromWarningToError()
|
||||
{
|
||||
// setup the test object
|
||||
TestLogger test = new TestLogger()
|
||||
{
|
||||
TraceSource = MethodInfo.GetCurrentMethod().Name,
|
||||
};
|
||||
TestTracingLevelChangeFromWarningToError(test);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests to verify that upon changing TracingLevel from Error To Warning,
|
||||
/// after the change, messages of Warning as well as of Error type are present in the log.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LoggerTracingLevelFromErrorToWarning()
|
||||
{
|
||||
// setup the test object
|
||||
TestLogger test = new TestLogger()
|
||||
{
|
||||
TraceSource = MethodInfo.GetCurrentMethod().Name,
|
||||
};
|
||||
TestTracingLevelChangeFromErrorToWarning(test);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When not use TraceSource, test to verify that upon changing TracingLevel from Warning To Error,
|
||||
/// after the change, messages of Error type are present in the log and those logged with warning type are not present.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LoggerNoTraceSourceTracingLevelFromWarningToError()
|
||||
{
|
||||
// setup the test object
|
||||
TestLogger test = new TestLogger()
|
||||
{
|
||||
TraceSource = MethodInfo.GetCurrentMethod().Name,
|
||||
DoNotUseTraceSource = true,
|
||||
};
|
||||
TestTracingLevelChangeFromWarningToError(test);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When not use TraceSource, test to verify that upon changing TracingLevel from Error To Warning,
|
||||
/// after the change, messages of Warning as well as of Error type are present in the log.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LoggerNoTraceSourceTracingLevelFromErrorToWarning()
|
||||
{
|
||||
// setup the test object
|
||||
TestLogger test = new TestLogger()
|
||||
{
|
||||
TraceSource = MethodInfo.GetCurrentMethod().Name,
|
||||
DoNotUseTraceSource = true,
|
||||
};
|
||||
TestTracingLevelChangeFromErrorToWarning(test);
|
||||
}
|
||||
|
||||
private static void TestTracingLevelChangeFromWarningToError(TestLogger test)
|
||||
{
|
||||
test.Initialize();
|
||||
Logger.TracingLevel = SourceLevels.Warning;
|
||||
string oldMessage = @"Old Message with Tracing Level set to Warning";
|
||||
test.LogMessage = oldMessage;
|
||||
// Initially with TracingLevel at Warning, logging of Warning type does not get filtered out.
|
||||
Assert.Equal(SourceLevels.Warning, Logger.TracingLevel);
|
||||
{
|
||||
test.EventType = TraceEventType.Warning;
|
||||
test.Write();
|
||||
test.PendingVerifications.Add(() =>
|
||||
{
|
||||
test.Verify(eventType: TraceEventType.Warning, message: oldMessage, callstackMessage: null, shouldVerifyCallstack: false, expectLogMessage: true);
|
||||
});
|
||||
}
|
||||
// and logging of Error type also succeeeds
|
||||
{
|
||||
test.EventType = TraceEventType.Error;
|
||||
test.Write();
|
||||
test.PendingVerifications.Add(() =>
|
||||
{
|
||||
test.Verify(eventType: TraceEventType.Error, message: oldMessage, callstackMessage: null, shouldVerifyCallstack: false, expectLogMessage: true);
|
||||
});
|
||||
}
|
||||
|
||||
//Now Update the tracing level to Error. Now logging both of Warning type gets filtered and only Error type should succeed.
|
||||
Logger.TracingLevel = SourceLevels.Error;
|
||||
Assert.Equal(SourceLevels.Error, Logger.TracingLevel);
|
||||
string newMessage = @"New Message After Tracing Level set to Error";
|
||||
test.LogMessage = newMessage;
|
||||
|
||||
// Now with TracingLevel at Error, logging of Warning type gets filtered out.
|
||||
{
|
||||
test.EventType = TraceEventType.Warning;
|
||||
test.Write();
|
||||
test.PendingVerifications.Add(() =>
|
||||
{
|
||||
test.Verify(eventType: TraceEventType.Warning, message: newMessage, callstackMessage: null, shouldVerifyCallstack: false, expectLogMessage: false);
|
||||
});
|
||||
}
|
||||
// but logging of Error type succeeds
|
||||
{
|
||||
test.EventType = TraceEventType.Error;
|
||||
test.Write();
|
||||
test.PendingVerifications.Add(() =>
|
||||
{
|
||||
test.Verify(eventType: TraceEventType.Error, message: newMessage, callstackMessage: null, shouldVerifyCallstack: false, expectLogMessage: true);
|
||||
});
|
||||
}
|
||||
|
||||
test.VerifyPending();
|
||||
test.Cleanup();
|
||||
}
|
||||
|
||||
private static void TestTracingLevelChangeFromErrorToWarning(TestLogger test)
|
||||
{
|
||||
test.Initialize();
|
||||
Logger.TracingLevel = SourceLevels.Error;
|
||||
string oldMessage = @"Old Message with Tracing Level set to Error";
|
||||
test.LogMessage = oldMessage;
|
||||
// Initially with TracingLevel at Error, logging of Warning type gets filtered out.
|
||||
Assert.Equal(SourceLevels.Error, Logger.TracingLevel);
|
||||
{
|
||||
test.EventType = TraceEventType.Warning;
|
||||
test.Write();
|
||||
test.PendingVerifications.Add(() =>
|
||||
{
|
||||
test.Verify(eventType: TraceEventType.Warning, message: oldMessage, callstackMessage: null, shouldVerifyCallstack: false, expectLogMessage: false);
|
||||
});
|
||||
}
|
||||
// But logging of Error type succeeeds
|
||||
{
|
||||
test.EventType = TraceEventType.Error;
|
||||
test.Write();
|
||||
test.PendingVerifications.Add(() =>
|
||||
{
|
||||
test.Verify(eventType: TraceEventType.Error, message: oldMessage, callstackMessage: null, shouldVerifyCallstack: false, expectLogMessage: true);
|
||||
});
|
||||
}
|
||||
|
||||
//Now Update the tracing level to Warning. Now logging both of Error type and Warning type should succeed.
|
||||
Logger.TracingLevel = SourceLevels.Warning;
|
||||
Assert.Equal(SourceLevels.Warning, Logger.TracingLevel);
|
||||
string newMessage = @"New Message After Tracing Level set to Warning";
|
||||
test.LogMessage = newMessage;
|
||||
|
||||
// Now with TracingLevel at Warning, logging of Warning type does not get filtered out.
|
||||
{
|
||||
test.EventType = TraceEventType.Warning;
|
||||
test.Write();
|
||||
test.PendingVerifications.Add(() =>
|
||||
{
|
||||
test.Verify(eventType: TraceEventType.Warning, message: newMessage, callstackMessage: null, shouldVerifyCallstack: false, expectLogMessage: true);
|
||||
});
|
||||
}
|
||||
// and logging of Error type also succeeds
|
||||
{
|
||||
test.EventType = TraceEventType.Error;
|
||||
test.Write();
|
||||
test.PendingVerifications.Add(() =>
|
||||
{
|
||||
test.Verify(eventType: TraceEventType.Error, message: newMessage, callstackMessage: null, shouldVerifyCallstack: false, expectLogMessage: true);
|
||||
});
|
||||
}
|
||||
|
||||
test.VerifyPending();
|
||||
test.Cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,29 +14,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
/// </summary>
|
||||
public class CommandOptionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void LoggingEnabledWhenFlagProvided()
|
||||
{
|
||||
var args = new string[] {"--enable-logging"};
|
||||
ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args);
|
||||
Assert.NotNull(options);
|
||||
|
||||
Assert.True(options.EnableLogging);
|
||||
Assert.False(options.ShouldExit);
|
||||
Assert.Equal(options.Locale, string.Empty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoggingDisabledWhenFlagNotProvided()
|
||||
{
|
||||
var args = new string[] {};
|
||||
ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args);
|
||||
Assert.NotNull(options);
|
||||
|
||||
Assert.False(options.EnableLogging);
|
||||
Assert.False(options.ShouldExit);
|
||||
Assert.Equal(options.Locale, string.Empty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UsageIsShownWhenHelpFlagProvided()
|
||||
@@ -63,16 +40,56 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
[Fact]
|
||||
public void DefaultValuesAreUsedWhenNoArgumentsAreProvided()
|
||||
{
|
||||
var args = new string[] {};
|
||||
ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args);
|
||||
Assert.NotNull(options);
|
||||
|
||||
Assert.False(options.EnableLogging);
|
||||
Assert.False(options.ShouldExit);
|
||||
Assert.True(string.IsNullOrWhiteSpace(options.LoggingDirectory));
|
||||
Assert.Equal(options.Locale, string.Empty);
|
||||
int? testNo = 1;
|
||||
// Test 1: All defaults, no options specified
|
||||
{
|
||||
var args = new string[] { };
|
||||
ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args);
|
||||
VerifyCommandOptions(options, testNo++);
|
||||
}
|
||||
// Test 2: All defaults, -logDir as null
|
||||
{
|
||||
var args = new string[] { "--log-dir", null };
|
||||
ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args);
|
||||
VerifyCommandOptions(options, testNo++);
|
||||
}
|
||||
// Test 3: All defaults, -logDir as empty string
|
||||
{
|
||||
var args = new string[] { "--log-dir", string.Empty };
|
||||
ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args);
|
||||
VerifyCommandOptions(options, testNo++);
|
||||
}
|
||||
// Test 4: All defaults, -log-file as null
|
||||
{
|
||||
var args = new string[] { "--log-file", null };
|
||||
ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args);
|
||||
VerifyCommandOptions(options, testNo++, logFilePath: null);
|
||||
}
|
||||
// Test 5: All defaults, -log-file as empty string
|
||||
{
|
||||
var args = new string[] { "--log-file", string.Empty };
|
||||
ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args);
|
||||
VerifyCommandOptions(options, testNo++, logFilePath: string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void VerifyCommandOptions(ServiceLayerCommandOptions options, int? testNo = null, string errorMessage = "", string tracingLevel = null, string logFilePath = null, bool shouldExit = false, string locale = "", string logDirectory = null)
|
||||
{
|
||||
Assert.NotNull(options);
|
||||
string MsgPrefix = testNo != null ? $"TestNo:{testNo} ::" : string.Empty;
|
||||
Assert.True(errorMessage == options.ErrorMessage, $"{MsgPrefix} options:{nameof(errorMessage)} should be '{errorMessage}'");
|
||||
Assert.True(tracingLevel == options.TracingLevel, $"{MsgPrefix} options:{nameof(tracingLevel)} should be '{tracingLevel}'");
|
||||
Assert.True(logFilePath == options.LogFilePath, $"{MsgPrefix} options:{nameof(logFilePath)} should be '{logFilePath}'");
|
||||
Assert.True(shouldExit == options.ShouldExit, $"{MsgPrefix} options:{nameof(shouldExit)} should be '{shouldExit}'");
|
||||
Assert.False(string.IsNullOrWhiteSpace(options.LoggingDirectory));
|
||||
if (string.IsNullOrWhiteSpace(logDirectory))
|
||||
{
|
||||
logDirectory = Path.Combine(options.DefaultLogRoot, options.ServiceName);
|
||||
}
|
||||
Assert.True(logDirectory == options.LoggingDirectory, $"{MsgPrefix} options:{nameof(logDirectory)} should be '{logDirectory}'");
|
||||
Assert.True(options.Locale == locale, $"{MsgPrefix} options:{nameof(locale)} should be '{locale}'");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("en")]
|
||||
[InlineData("es")]
|
||||
@@ -107,7 +124,6 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
|
||||
// Asserting all options were properly set
|
||||
Assert.NotNull(options);
|
||||
Assert.False(options.EnableLogging);
|
||||
Assert.False(options.ShouldExit);
|
||||
Assert.Equal(options.Locale, string.Empty);
|
||||
}
|
||||
@@ -124,5 +140,33 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
|
||||
Assert.False(options.ShouldExit);
|
||||
Assert.Equal(options.LoggingDirectory, logDir);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void TracingLevelSet()
|
||||
{
|
||||
string expectedLevel = "Critical";
|
||||
var args = new string[] { "--tracing-level", expectedLevel };
|
||||
ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args);
|
||||
|
||||
// Asserting all options were properly set
|
||||
Assert.NotNull(options);
|
||||
Assert.False(options.ShouldExit);
|
||||
Assert.Equal(options.TracingLevel, expectedLevel);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void LogFilePathSet()
|
||||
{
|
||||
string expectedFilePath = Path.GetRandomFileName();
|
||||
var args = new string[] { "--log-file", expectedFilePath };
|
||||
ServiceLayerCommandOptions options = new ServiceLayerCommandOptions(args);
|
||||
|
||||
// Asserting all options were properly set
|
||||
Assert.NotNull(options);
|
||||
Assert.False(options.ShouldExit);
|
||||
Assert.Equal(options.LogFilePath, expectedFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
test/ScriptGenerator/AdventureWorks.sql
Normal file
BIN
test/ScriptGenerator/AdventureWorks.sql
Normal file
Binary file not shown.
BIN
test/ScriptGenerator/AdventureWorksDbCreate.sql
Normal file
BIN
test/ScriptGenerator/AdventureWorksDbCreate.sql
Normal file
Binary file not shown.
BIN
test/ScriptGenerator/AdventureWorksDbReadWrite.sql
Normal file
BIN
test/ScriptGenerator/AdventureWorksDbReadWrite.sql
Normal file
Binary file not shown.
BIN
test/ScriptGenerator/AdventureWorksStoredProceduresCreate.sql
Normal file
BIN
test/ScriptGenerator/AdventureWorksStoredProceduresCreate.sql
Normal file
Binary file not shown.
BIN
test/ScriptGenerator/AdventureWorksTablesCreate.sql
Normal file
BIN
test/ScriptGenerator/AdventureWorksTablesCreate.sql
Normal file
Binary file not shown.
BIN
test/ScriptGenerator/AdventureWorksViewsCreate.sql
Normal file
BIN
test/ScriptGenerator/AdventureWorksViewsCreate.sql
Normal file
Binary file not shown.
160
test/ScriptGenerator/CommandOptions.cs
Normal file
160
test/ScriptGenerator/CommandOptions.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace ScriptGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// The command-line options helper class.
|
||||
/// </summary>
|
||||
public class CommandOptions
|
||||
{
|
||||
private const string DefaultFilePrefix = "AdventureWorks";
|
||||
/// <summary>
|
||||
/// Construct and parse command line options from the arguments array
|
||||
/// </summary>
|
||||
public CommandOptions(string[] args)
|
||||
{
|
||||
ErrorMessage = string.Empty;
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < args.Length; ++i)
|
||||
{
|
||||
string arg = args[i];
|
||||
if (arg.StartsWith("--") || arg.StartsWith("-"))
|
||||
{
|
||||
// Extracting arguments and properties
|
||||
arg = arg.Substring(1).ToLowerInvariant();
|
||||
string argName = arg;
|
||||
switch (argName)
|
||||
{
|
||||
case "-filepathprefix":
|
||||
case "fp":
|
||||
FilePathPrefix = args[++i];
|
||||
break;
|
||||
case "-numberofdatabases":
|
||||
case "-numberofdbs":
|
||||
case "ndb":
|
||||
{
|
||||
if (int.TryParse(args[++i], out int n))
|
||||
{
|
||||
Databases = n;
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorMessage += $@"Argument for NumberOfDatabases:'{args[i]}' is not a valid integer";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "-tablesmultiplier":
|
||||
case "tm":
|
||||
{
|
||||
if (int.TryParse(args[++i], out int n))
|
||||
{
|
||||
TablesMultiplier = n;
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorMessage += $@"Argument for NumberOfTables:'{args[i]}' is not a valid integer";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "-storedproceduresmultiplier":
|
||||
case "spm":
|
||||
{
|
||||
if (int.TryParse(args[++i], out int n))
|
||||
{
|
||||
StoredProceduresMultiplier = n;
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorMessage += $@"Argument for NumberOfStoredProcedures:'{args[i]}' is not a valid integer";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "-viewsmultiplier":
|
||||
case "vm":
|
||||
{
|
||||
if (int.TryParse(args[++i], out int n))
|
||||
{
|
||||
ViewsMultiplier = n;
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorMessage += $@"Argument for NumberOfTables:'{args[i]}' is not a valid integer";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "h":
|
||||
case "-help":
|
||||
ShouldExit = true;
|
||||
return;
|
||||
default:
|
||||
ErrorMessage += string.Format("Unknown argument \"{0}\"" + Environment.NewLine, argName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorMessage += ex.ToString();
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ErrorMessage) || ShouldExit)
|
||||
{
|
||||
Console.WriteLine(Usage);
|
||||
ShouldExit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains any error messages during execution
|
||||
/// </summary>
|
||||
public string ErrorMessage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the program should exit immediately. Set to true when the usage is printed.
|
||||
/// </summary>
|
||||
public bool ShouldExit { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the usage string describing command-line arguments for the program
|
||||
/// </summary>
|
||||
public string Usage
|
||||
{
|
||||
get
|
||||
{
|
||||
var str = $@"{ErrorMessage}" + Environment.NewLine +
|
||||
" Options:" + Environment.NewLine +
|
||||
$@" [--filepathprefix **] (default {DefaultFilePrefix})" + Environment.NewLine +
|
||||
$@" [-fp **] (default {DefaultFilePrefix})" + Environment.NewLine +
|
||||
" [--NumberOfDatabases **] (default 1)" + Environment.NewLine +
|
||||
" [-ndb **] (default 1)" + Environment.NewLine +
|
||||
" [--TablesMultiplier **] (default 1)" + Environment.NewLine +
|
||||
" [-tm **] (default 1)" + Environment.NewLine +
|
||||
" [--ViewsMultiplier **] (default 1)" + Environment.NewLine +
|
||||
" [-vm **] (default 1)" + Environment.NewLine +
|
||||
" [--StoreProceduresMultiplier **] (default 1)" + Environment.NewLine +
|
||||
" [-spm **] (default 1)" + Environment.NewLine +
|
||||
" [--help]" + Environment.NewLine;
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
public int Databases { get; private set; } = 1;
|
||||
public int TablesMultiplier { get; private set; } = 1;
|
||||
public int StoredProceduresMultiplier { get; private set; } = 1;
|
||||
public int ViewsMultiplier { get; private set; } = 1;
|
||||
public string FilePathPrefix { get; private set; } = DefaultFilePrefix;
|
||||
}
|
||||
}
|
||||
57
test/ScriptGenerator/Program.cs
Normal file
57
test/ScriptGenerator/Program.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using ScriptGenerator.Properties;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ScriptGenerator
|
||||
{
|
||||
|
||||
class Program
|
||||
{
|
||||
internal const string DefaultFileExtension = "Sql";
|
||||
static readonly Regex ExtractDbName = new Regex(@"CREATE\s+DATABASE\s+\[(?<dbName>\w+)\]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
static readonly Regex CreateTableRegex = new Regex(@"(?<begin>CREATE\s+TABLE\s+.*)(?<middle>\](?![\.])[\s\S]*?CONSTRAINT\s+\[\w+?)(?<end>\](?![\.]))", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
static readonly Regex AlterTableConstraintRegex = new Regex(@"(?<begin>ALTER\s+TABLE\s+.*?)(?<middle>\](?![\.])\s*\b(?:ADD|WITH|CHECK)\b[\s\S]*?CONSTRAINT\s+\[\w+?)(?<end>\](?![\.]))", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
static readonly Regex CreateViewRegex = new Regex(@"(?<begin>CREATE\s+VIEW\s+.*?)(?<end>\](?![\.])[\s\S]*?\bAS\b)", RegexOptions.Compiled | RegexOptions.IgnoreCase );
|
||||
static readonly Regex CreateProcedureRegex = new Regex(@"(?<begin>CREATE\s+PROCEDURE\s+.*?)(?<end>\](?![\.])[\s\S]*?(?:@|WITH|AS))", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
static void Main(string[] args)
|
||||
{
|
||||
CommandOptions options = new CommandOptions(args);
|
||||
for (int d = 1; d <= options.Databases; d++)
|
||||
{
|
||||
using (StreamWriter writer = new StreamWriter(Path.ChangeExtension($@"{options.FilePathPrefix}{d}", DefaultFileExtension)))
|
||||
{
|
||||
string oldDbName = ExtractDbName.Match(Resources.AdventureWorks).Groups["dbName"].Value;
|
||||
string newDbName = $@"{oldDbName}{d}";
|
||||
//Turn Off Strict Clr Mode
|
||||
writer.WriteLine(Resources.TurnOffClrStrictSecurityMode);
|
||||
|
||||
//Put all original objects in the 'newDbName' database
|
||||
writer.WriteLine(Resources.AdventureWorks.Replace(oldDbName, newDbName));
|
||||
|
||||
// Multiple copies of Create Table statements
|
||||
for (int t = 1; t <= options.TablesMultiplier; t++)
|
||||
{
|
||||
string tablesScript = Resources.AdventureWorksTablesCreate.Replace(oldDbName, newDbName);
|
||||
tablesScript = CreateTableRegex.Replace(tablesScript, "${begin}" + t + "${middle}" + t + "${end}");
|
||||
writer.WriteLine(AlterTableConstraintRegex.Replace(tablesScript, "${begin}"+ t + "${middle}" + t + "${end}"));
|
||||
}
|
||||
// Multiple copies of Create View statements
|
||||
for (int v = 1; v <= options.ViewsMultiplier; v++)
|
||||
{
|
||||
string viewScript = Resources.AdventureWorksViewsCreate.Replace(oldDbName, newDbName);
|
||||
writer.WriteLine(CreateViewRegex.Replace(viewScript, "${begin}" + v + "${end}"));
|
||||
}
|
||||
// Multiple copies of Create Procedure statements
|
||||
for (int s = 1; s <= options.StoredProceduresMultiplier; s++)
|
||||
{
|
||||
string spScript = Resources.AdventureWorksStoredProceduresCreate.Replace(oldDbName, newDbName);
|
||||
writer.WriteLine(CreateProcedureRegex.Replace(spScript, "${begin}" + s + "${end}"));
|
||||
}
|
||||
//Turn On Strict Clr Mode
|
||||
writer.WriteLine(Resources.TurnOnClrStrictSecurityMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
139
test/ScriptGenerator/Properties/Resources.resx
Normal file
139
test/ScriptGenerator/Properties/Resources.resx
Normal file
@@ -0,0 +1,139 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="AdventureWorks" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\AdventureWorks.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value>
|
||||
</data>
|
||||
<data name="AdventureWorksStoredProceduresCreate" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\AdventureWorksStoredProceduresCreate.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value>
|
||||
</data>
|
||||
<data name="AdventureWorksTablesCreate" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\AdventureWorksTablesCreate.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value>
|
||||
</data>
|
||||
<data name="AdventureWorksViewsCreate" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\AdventureWorksViewsCreate.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value>
|
||||
</data>
|
||||
<data name="TurnOffClrStrictSecurityMode" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\TurnOffClrStrictSecurityMode.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value>
|
||||
</data>
|
||||
<data name="TurnOnClrStrictSecurityMode" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\TurnOnClrStrictSecurityMode.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value>
|
||||
</data>
|
||||
</root>
|
||||
8
test/ScriptGenerator/Properties/launchSettings.json
Normal file
8
test/ScriptGenerator/Properties/launchSettings.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"profiles": {
|
||||
"ScriptGenerator": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "-vm 59 -spm 8 -tm 15 -ndb 200"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
test/ScriptGenerator/ScriptGenerator.csproj
Normal file
23
test/ScriptGenerator/ScriptGenerator.csproj
Normal file
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
BIN
test/ScriptGenerator/TurnOffClrStrictSecurityMode.sql
Normal file
BIN
test/ScriptGenerator/TurnOffClrStrictSecurityMode.sql
Normal file
Binary file not shown.
BIN
test/ScriptGenerator/TurnOnClrStrictSecurityMode.sql
Normal file
BIN
test/ScriptGenerator/TurnOnClrStrictSecurityMode.sql
Normal file
Binary file not shown.
Reference in New Issue
Block a user