TestExecWindow
 All Modules Pages
TestExecWindow.cs
//------------------------------------------------------------------------------
// Copyright(C) 2016 Gerald Fahrnholz
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// Contact: http://www.gerald-fahrnholz.eu/impressum.php
//------------------------------------------------------------------------------
namespace TestExecWin
{
using System;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
public enum AppType { NO_TEST_FOUND, TTB, BOOST };
public interface IMainEvents
{
void OnRefreshAll();
void OnRefreshNow();
void OnSetOutputLevel(int in_level);
void OnTestTerminated(bool in_success, string in_args, bool in_memLeaksDetected, TimeSpan in_executionTime);
void OnStartDebugging(string in_cmdLineParams);
void OnOpenProtocolFile();
void OnOpenSourceFile(string in_fullFileName, int in_lineNum);
string OnGetExecutablesFromCurrentSolution();
}
public interface IExecute
{
void SetMemLeakCheck(bool in_state);
void StartProcess(string exePath, string args, string workDir);
void KillRunningProcess();
}
public interface IEventReceiver
{
void WriteLine(int in_outputLevel, String in_info);
void MsgBox(String in_info);
}
public class TestGroupEntry
{
public bool isBoostGroup { get; set; }
public string fileFullPath { get; set; }
public int lineNum { get; set; }
public System.Collections.Generic.List<string> testGroups; // fulll hierarchy of test groups
public TestGroupEntry(bool in_isBoostGroup)
{
isBoostGroup = in_isBoostGroup;
testGroups = new System.Collections.Generic.List<string>();
}
public void AddTestGroupHierarchy(System.Collections.Generic.List<string> in_testGroupHierarchy)
{
foreach (string grp in in_testGroupHierarchy)
{
testGroups.Add(grp);
}
}
public string GetTestGroupHierarchyString()
{
string allTestGroups = "";
foreach (string grp in testGroups)
{
if (allTestGroups != "")
{
allTestGroups += "/";
}
allTestGroups += grp;
}
return allTestGroups;
}
public string GetDisplayString()
{
string prefix = "";
for (int i = 1; i < testGroups.Count; ++i)
{
prefix += " ";
}
if (testGroups.Count == 0)
return prefix + "<default>";
else
return prefix + testGroups[testGroups.Count - 1];
}
public string GetCmdString()
{
string cmdString = GetTestGroupHierarchyString();
if (cmdString != "")
{
cmdString = (isBoostGroup ? "--run_test=" : "-selectTestFile ") + cmdString;
}
return cmdString;
}
}
public class TestFuncEntry
{
public bool isBoostFunc { get; set; }
public string testFunc { get; set; }
public string fileFullPath { get; set; }
public int lineNum { get; set; }
public System.Collections.Generic.List<string> testGroups; // hierarchy of test groups
public TestFuncEntry(bool in_isBoostFunc)
{
isBoostFunc = in_isBoostFunc;
testFunc = "";
fileFullPath = "";
lineNum = 0;
testGroups = new System.Collections.Generic.List<string>(); // hierarchy of test groups
}
public void AddTestGroupHierarchy(System.Collections.Generic.List<string> in_testGroupHierarchy)
{
foreach (string grp in in_testGroupHierarchy)
{
testGroups.Add(grp);
}
}
public string GetTestGroupHierarchyString()
{
string allTestGroups = "";
foreach (string grp in testGroups)
{
if (allTestGroups != "")
{
allTestGroups += "/";
}
allTestGroups += grp;
}
return allTestGroups;
}
public string GetDisplayString()
{
return testFunc;
}
public string GetCmdString()
{
if (isBoostFunc)
{
string cmdString = GetTestGroupHierarchyString();
if (cmdString != "")
{
cmdString += "/";
}
cmdString += testFunc;
return "--run_test=" + cmdString;
}
else // TTB
{
return "-selectTestFunc " + testFunc;
}
}
}
public class ProjectInfo
{
public string solutionFullPath { get; set; }
public string project { get; set; }
public string config { get; set; }
public string sourceDirPath { get; set; }
public string targetDirPath { get; set; }
public string fullExePath { get; set; }
public AppType appType { get; set; }
public System.Collections.Generic.List<TestGroupEntry> testGroups = new System.Collections.Generic.List<TestGroupEntry>();
public System.Collections.Generic.List<TestFuncEntry> testFuncs = new System.Collections.Generic.List<TestFuncEntry>();
public ProjectInfo()
{
solutionFullPath = "<not set>";
project = "<not set>";
config = "<not set>";
sourceDirPath = "<not set>";
targetDirPath = "<not set>";
fullExePath = "<not set>";
appType = AppType.NO_TEST_FOUND;
}
public string GetExePath()
{
return fullExePath;
}
}
class Services
{
public static string GetThreadIdPrompt()
{
int tid = System.Threading.Thread.CurrentThread.ManagedThreadId;
string msg = "TID " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString() + ": ";
return msg;
}
};
/// <summary>
/// This class implements the tool window exposed by this package and hosts a user control.
/// </summary>
/// <remarks>
/// In Visual Studio tool windows are composed of a frame (implemented by the shell) and a pane,
/// usually implemented by the package implementer.
/// <para>
/// This class derives from the ToolWindowPane class provided from the MPF in order to use its
/// implementation of the IVsUIElementPane interface.
/// </para>
/// </remarks>
[Guid("7c0e601f-3680-4cfd-b2bb-b774e3119f4b")]
public class TestExecWindow : ToolWindowPane, IEventReceiver, IMainEvents
{
// Helper objects with specific tasks
private VisualStudioConnector m_vsConnector;
private SourceFileParser m_parser;
private Executor m_executor;
private int m_outputLevel = 2;
private bool m_writeThreadId = false;
private ProjectInfo m_projectInfo = new ProjectInfo();
/// <summary>
/// Initializes a new instance of the <see cref="TestExecWindow"/> class.
/// </summary>
public TestExecWindow() : base(null)
{
m_executor = new Executor(this, this);
this.Caption = "TestExecWindow";
// This is the user control hosted by the tool window; Note that, even if this class implements IDisposable,
// we are not calling Dispose on this object. This is because ToolWindowPane calls Dispose on
// the object returned by the Content property.
this.Content = new TestExecWindowControl(this, this, m_executor);
m_vsConnector = new VisualStudioConnector(this, this);
m_parser = new SourceFileParser(this);
RefreshAll();
}
//}
protected override void Dispose(bool disposing)
{
m_vsConnector.DisconnectFromVisualStudio();
base.Dispose(disposing);
}
private void RefreshAll()
{
WriteLine(3, "RefreshAll-Begin");
try
{
// Reset data
m_projectInfo = new ProjectInfo();
if (!m_vsConnector.ReadSettingsOfStartupProject(m_projectInfo))
{
// MsgBox("There is no startup project defined or the project settings are not accessible!");
WriteLine(2,"WARNING: There is no startup project defined or the project settings are not accessible!");
return;
}
m_parser.ScanProjectDir(m_projectInfo);
Gui().SetTestInfo(m_projectInfo);
WriteLine(1, "Detected project " + m_projectInfo.project + " (" + m_projectInfo.config + ")"
+ ", type is " + m_projectInfo.appType
+ ", " + m_projectInfo.testGroups.Count + " test groups, "
+ m_projectInfo.testFuncs.Count + " test funcs");
}
catch (Exception e)
{
string info = "EXCEPTION: " + e.ToString();
WriteLine(1, info);
}
WriteLine(3, "RefreshAll-End");
}
public void WriteLine(int in_outputLevel, String in_info)
{
if (in_outputLevel <= m_outputLevel)
{
if (m_writeThreadId)
{
Gui().AddInfoToEventList(Services.GetThreadIdPrompt() + in_info);
}
else
{
Gui().AddInfoToEventList(in_info);
}
}
}
TestExecWindowControl Gui()
{
return (TestExecWindowControl)this.Content;
}
// Someone wants to refresh
public void OnRefreshAll()
{
// Synchronize via GUI thread
Gui().TriggerDelayedRefresh();
}
// After synchronizing GUI calls this method
public void OnRefreshNow()
{
// If test is still running we do not want to remove current data
if (Gui().TestIsRunning())
{
string info = "WARNING: Ignoring change of startup project while test is running";
WriteLine(1, info);
return;
}
// We assume to be within safe GUI thread
RefreshAll();
}
public void OnSetOutputLevel(int in_level)
{
m_outputLevel = in_level;
m_writeThreadId = m_outputLevel >= 2;
}
/// [test terminated]
public void OnTestTerminated(bool in_success, string in_args, bool in_memLeaksDetected, TimeSpan in_executionTime)
{
// This function is called when the test app terminates.
// The call may arrive on any system thread and needs to be synchronized.
// Simply pass all data to GUI where the main GUI thread (dispatcher thread)
// can be used for synchronization.
Gui().TestTerminated(in_success, in_args, in_memLeaksDetected, in_executionTime);
}
/// [test terminated]
public void OnStartDebugging(string in_cmdLineParams)
{
m_vsConnector.StartDebugging(in_cmdLineParams);
}
public void OnOpenProtocolFile()
{
string protocolFilePath = m_projectInfo.GetExePath();
protocolFilePath = protocolFilePath.Replace(".exe", ".out");
if (!System.IO.File.Exists(protocolFilePath))
{
MsgBox("Test protocol not found: " + protocolFilePath);
}
else
{
m_vsConnector.OpenFile(protocolFilePath);
}
}
public void OnOpenSourceFile(string in_fullFileName, int in_lineNum)
{
if (!System.IO.File.Exists(in_fullFileName))
{
MsgBox("Sourcefile not found: " + in_fullFileName);
}
else
{
m_vsConnector.OpenFile(in_fullFileName, in_lineNum);
}
}
public string OnGetExecutablesFromCurrentSolution()
{
return m_vsConnector.GetExecutablesFromCurrentSolution();
}
public void MsgBox(string in_info)
{
System.Windows.MessageBox.Show(in_info, "Info");
}
}
}