Files
sqltoolsservice/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ShowPlan/ShowPlanTests.cs
Lewis Sanchez 8215d88dcf Adds new graph comparison request handler to the Execution Plan Service. (#1438)
* Adds new graph comparison request handler to the Execution Plan Service.

* Code review changes

* Renames execution plan compare params, result, and request classes.
2022-03-22 14:52:42 -07:00

314 lines
13 KiB
C#

//
// 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.Collections.Generic;
using System.IO;
using System.Reflection;
using NUnit.Framework;
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan;
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.Contracts;
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan;
using Microsoft.SqlTools.ServiceLayer.ExecutionPlan.ShowPlan.Comparison;
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ShowPlan
{
public class ShowPlanXMLTests
{
private string queryPlanFileText;
[Test]
public void ParseXMLFileReturnsValidShowPlanGraph()
{
ReadFile(".ShowPlan.TestShowPlan.xml");
var showPlanGraphs = ExecutionPlanGraphUtils.CreateShowPlanGraph(queryPlanFileText, "testFile.sql");
Assert.AreEqual(1, showPlanGraphs.Count, "exactly one show plan graph should be returned");
Assert.NotNull(showPlanGraphs[0], "graph should not be null");
Assert.NotNull(showPlanGraphs[0].Root, "graph should have a root");
}
[Test]
public void CompareShowPlan_CreateSkeleton()
{
ReadFile(".ShowPlan.TestShowPlan.xml");
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
var rootNode = graphs[0].Root;
Assert.NotNull(rootNode, "graph should have a root");
var skeletonManager = new SkeletonManager();
var skeletonNode = skeletonManager.CreateSkeleton(rootNode);
Assert.NotNull(skeletonNode, "skeletonNode should not be null");
}
[Test]
public void CompareShowPlan_CreateSkeletonUsingDefaultConfiguredNode()
{
var graph = new ShowPlanGraph()
{
Root = null,
Description = new Description()
};
var context = new NodeBuilderContext(graph, ShowPlanType.Unknown, null);
var node = new Node(default, context);
var skeletonManager = new SkeletonManager();
var skeletonNode = skeletonManager.CreateSkeleton(node);
Assert.NotNull(skeletonNode, "skeletonNode should not be null");
}
[Test]
public void CompareShowPlan_DuplicateSkeletons()
{
ReadFile(".ShowPlan.TestShowPlan.xml");
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
var rootNode = graphs[0].Root;
var skeletonManager = new SkeletonManager();
var skeletonNode = skeletonManager.CreateSkeleton(rootNode);
var skeletonNode2 = skeletonManager.CreateSkeleton(rootNode);
var skeletonCompareResult = skeletonManager.AreSkeletonsEquivalent(skeletonNode, skeletonNode2, ignoreDatabaseName: true);
Assert.IsTrue(skeletonCompareResult);
}
[Test]
public void CompareShowPlan_TwoDefaultConfiguredSkeletons()
{
var graph = new ShowPlanGraph()
{
Root = null,
Description = new Description()
};
var context = new NodeBuilderContext(graph, ShowPlanType.Unknown, null);
var firstRoot = new Node(default, context);
var secondRoot = new Node(default, context);
var skeletonManager = new SkeletonManager();
var firstSkeletonNode = skeletonManager.CreateSkeleton(firstRoot);
var secondSkeletonNode = skeletonManager.CreateSkeleton(secondRoot);
var skeletonCompareResult = skeletonManager.AreSkeletonsEquivalent(firstSkeletonNode, secondSkeletonNode, ignoreDatabaseName: true);
Assert.IsTrue(skeletonCompareResult, "The two compared skeleton nodes should be equivalent");
}
[Test]
public void CompareShowPlan_ComparingSkeletonAgainstNull()
{
ReadFile(".ShowPlan.TestShowPlan.xml");
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
var rootNode = graphs[0].Root;
var skeletonManager = new SkeletonManager();
var skeletonNode = skeletonManager.CreateSkeleton(rootNode);
SkeletonNode skeletonNode2 = null;
var skeletonCompareResult = skeletonManager.AreSkeletonsEquivalent(skeletonNode, skeletonNode2, ignoreDatabaseName: true);
Assert.IsFalse(skeletonCompareResult);
}
[Test]
public void CompareShowPlan_DifferentSkeletonChildCount()
{
ReadFile(".ShowPlan.TestShowPlan.xml");
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
var rootNode = graphs[0].Root;
var skeletonManager = new SkeletonManager();
var skeletonNode = skeletonManager.CreateSkeleton(rootNode);
var skeletonNode2 = skeletonManager.CreateSkeleton(rootNode);
skeletonNode2.Children.RemoveAt(skeletonNode2.Children.Count - 1);
var skeletonCompareResult = skeletonManager.AreSkeletonsEquivalent(skeletonNode, skeletonNode2, ignoreDatabaseName: true);
Assert.IsFalse(skeletonCompareResult);
}
[Test]
public void CompareShowPlan_ColorMatchingSectionsWithEquivalentSkeletons()
{
ReadFile(".ShowPlan.TestShowPlan.xml");
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
var rootNode = graphs[0].Root;
var skeletonManager = new SkeletonManager();
var skeletonNode = skeletonManager.CreateSkeleton(rootNode);
var skeletonNode2 = skeletonManager.CreateSkeleton(rootNode);
Assert.IsFalse(skeletonNode.HasMatch);
Assert.IsFalse(skeletonNode2.HasMatch);
Assert.Zero(skeletonNode.MatchingNodes.Count);
Assert.Zero(skeletonNode2.MatchingNodes.Count);
skeletonManager.ColorMatchingSections(skeletonNode, skeletonNode2, ignoreDatabaseName: true);
Assert.IsTrue(skeletonNode.HasMatch);
Assert.IsTrue(skeletonNode2.HasMatch);
Assert.AreEqual(1, skeletonNode.MatchingNodes.Count);
Assert.AreEqual(1, skeletonNode2.MatchingNodes.Count);
}
[Test]
public void CompareShowPlan_ColorMatchingSectionsWithNullAndNonNullSkeleton()
{
ReadFile(".ShowPlan.TestShowPlan.xml");
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
var rootNode = graphs[0].Root;
var skeletonManager = new SkeletonManager();
SkeletonNode nullSkeletonNode = null;
var skeletonNode2 = skeletonManager.CreateSkeleton(rootNode);
skeletonManager.ColorMatchingSections(nullSkeletonNode, skeletonNode2, ignoreDatabaseName: true);
Assert.IsNull(nullSkeletonNode);
Assert.IsFalse(skeletonNode2.HasMatch);
Assert.Zero(skeletonNode2.MatchingNodes.Count);
}
[Test]
public void CompareShowPlan_ColorMatchingSectionsWithTwoNullSKeletons()
{
var skeletonManager = new SkeletonManager();
SkeletonNode skeletonNode = null;
SkeletonNode skeletonNode2 = null;
skeletonManager.ColorMatchingSections(skeletonNode, skeletonNode2, ignoreDatabaseName: true);
Assert.IsNull(skeletonNode);
Assert.IsNull(skeletonNode2);
}
[Test]
public void CompareShowPlan_ColorMatchingSectionsWithNonNullAndNullSkeleton()
{
ReadFile(".ShowPlan.TestShowPlan.xml");
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
var rootNode = graphs[0].Root;
var skeletonManager = new SkeletonManager();
var skeletonNode = skeletonManager.CreateSkeleton(rootNode);
SkeletonNode nullSkeletonNode = null;
skeletonManager.ColorMatchingSections(skeletonNode, nullSkeletonNode, ignoreDatabaseName: true);
Assert.IsFalse(skeletonNode.HasMatch);
Assert.Zero(skeletonNode.MatchingNodes.Count);
Assert.IsNull(nullSkeletonNode);
}
[Test]
public void CompareShowPlan_ColorMatchingSectionsWithDifferentChildCount()
{
ReadFile(".ShowPlan.TestShowPlan.xml");
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
var rootNode = graphs[0].Root;
var skeletonManager = new SkeletonManager();
var skeletonNode = skeletonManager.CreateSkeleton(rootNode);
var skeletonNode2 = skeletonManager.CreateSkeleton(rootNode);
skeletonNode2.Children.RemoveAt(skeletonNode2.Children.Count - 1);
Assert.IsFalse(skeletonNode.Children[0].HasMatch);
Assert.IsFalse(skeletonNode2.Children[0].HasMatch);
Assert.Zero(skeletonNode.Children[0].MatchingNodes.Count);
Assert.Zero(skeletonNode2.Children[0].MatchingNodes.Count);
skeletonManager.ColorMatchingSections(skeletonNode, skeletonNode2, ignoreDatabaseName: true);
Assert.IsTrue(skeletonNode.Children[0].HasMatch);
Assert.IsTrue(skeletonNode2.Children[0].HasMatch);
Assert.AreEqual(1, skeletonNode.Children[0].MatchingNodes.Count);
Assert.AreEqual(1, skeletonNode2.Children[0].MatchingNodes.Count);
}
[Test]
public void CompareShowPlan_FindNextNonIgnoreNode()
{
ReadFile(".ShowPlan.TestShowPlan.xml");
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
var rootNode = graphs[0].Root;
var skeletonManager = new SkeletonManager();
var result = skeletonManager.FindNextNonIgnoreNode(rootNode);
Assert.NotNull(result);
Assert.AreEqual("InnerJoin", result.LogicalOpUnlocName);
}
[Test]
public void CompareShowPlan_FindNextNonIgnoreNodeWithChildlessRoot()
{
ReadFile(".ShowPlan.TestShowPlan.xml");
var graphs = ShowPlanGraph.ParseShowPlanXML(this.queryPlanFileText, ShowPlanType.Unknown);
var rootNode = graphs[0].Root;
for (int childIndex = 0; childIndex < rootNode.Children.Count; ++childIndex)
{
rootNode.Children.RemoveAt(0);
}
var skeletonManager = new SkeletonManager();
var result = skeletonManager.FindNextNonIgnoreNode(rootNode);
Assert.IsNull(result);
}
[Test]
public void CompareShowPlan_FindNextNonIgnoreNodeWithNullNode()
{
Node rootNode = null;
var skeletonManager = new SkeletonManager();
var result = skeletonManager.FindNextNonIgnoreNode(rootNode);
Assert.IsNull(result);
}
[Test]
public void ParsingNestedProperties()
{
ReadFile(".ShowPlan.TestShowPlan.xml");
string[] commonNestedPropertiesNames = { "MemoryGrantInfo", "OptimizerHardwareDependentProperties" };
var showPlanGraphs = ExecutionPlanGraphUtils.CreateShowPlanGraph(queryPlanFileText, "testFile.sql");
ExecutionPlanNode rootNode = showPlanGraphs[0].Root;
rootNode.Properties.ForEach(p =>
{
if (Array.FindIndex(commonNestedPropertiesNames, i => i.Equals(p.Name)) != -1 && (NestedExecutionPlanGraphProperty)p != null)
{
Assert.NotZero(((NestedExecutionPlanGraphProperty)p).Value.Count);
}
});
}
[Test]
public void ParseXMLFileWithRecommendations()
{
//The first graph in this execution plan has 3 recommendations
ReadFile(".ShowPlan.TestShowPlanRecommendations.xml");
string[] commonNestedPropertiesNames = { "MemoryGrantInfo", "OptimizerHardwareDependentProperties" };
var showPlanGraphs = ExecutionPlanGraphUtils.CreateShowPlanGraph(queryPlanFileText, "testFile.sql");
List<ExecutionPlanRecommendation> rootNode = showPlanGraphs[0].Recommendations;
Assert.AreEqual(3, rootNode.Count, "3 recommendations should be returned by the showplan parser");
}
private void ReadFile(string fileName)
{
Assembly assembly = Assembly.GetAssembly(typeof(ShowPlanXMLTests));
Stream scriptStream = assembly.GetManifestResourceStream(assembly.GetName().Name + fileName);
StreamReader reader = new StreamReader(scriptStream);
queryPlanFileText = reader.ReadToEnd();
}
}
}