aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiguel Costa <miguel.costa@qt.io>2019-04-08 18:25:43 +0200
committerMiguel Costa <miguel.costa@qt.io>2019-05-08 10:58:17 +0000
commit6fe2cfd41579384fb1983b0754724c606768f6fd (patch)
tree6fbd670b4f754d37c83e556126a966d1f97b2ed9
parentc238bd73ed42f8f396ad1cfd928bdad56b87f656 (diff)
Refactor auto-test macro module
Did some clean-up of the auto-test macro module, including making the code generation more readable and adding a unified set of error reporting functions/macros. Change-Id: I5441afcc14ff4cbd87d5a9c9aa7ff77d2dc2d057 Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
-rw-r--r--src/qtvstest/Macro.cs642
-rw-r--r--src/qtvstest/MacroClient.h27
2 files changed, 414 insertions, 255 deletions
diff --git a/src/qtvstest/Macro.cs b/src/qtvstest/Macro.cs
index a606dfe9..7c8b36f3 100644
--- a/src/qtvstest/Macro.cs
+++ b/src/qtvstest/Macro.cs
@@ -35,7 +35,6 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
-using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CSharp;
@@ -112,27 +111,64 @@ namespace QtVsTest.Macros
static MacroParser Parser { get; set; }
MacroLines MacroLines { get; set; }
+ List<string> SelectedAssemblies { get { return _SelectedAssemblies; } }
+ List<string> _SelectedAssemblies =
+ new List<string>(MSBuild.MetaInfo.QtVsTest.Reference)
+ {
+ "QtVsTest",
+ "System.Core",
+ };
+
IEnumerable<string> RefAssemblies { get; set; }
- IEnumerable<string> RefNamespaces { get; set; }
+
+ List<string> Namespaces { get { return _Namespaces; } }
+ List<string> _Namespaces =
+ new List<string>
+ {
+ "System",
+ "System.Linq",
+ "System.Reflection",
+ "Task = System.Threading.Tasks.Task",
+ "System.Windows.Automation",
+ "EnvDTE",
+ "EnvDTE80",
+ };
+
+ Dictionary<string, VSServiceRef> ServiceRefs { get { return _ServiceRefs; } }
+ Dictionary<string, VSServiceRef> _ServiceRefs =
+ new Dictionary<string, VSServiceRef>
+ {
+ {
+ "Dte", new VSServiceRef
+ { Name = "Dte", Interface = "DTE2", Type = "DTE" }
+ },
+ };
+
+ Dictionary<string, GlobalVar> GlobalVars { get { return _GlobalVars; } }
+ Dictionary<string, GlobalVar> _GlobalVars =
+ new Dictionary<string, GlobalVar>
+ {
+ {
+ "Result", new GlobalVar
+ { Type = "string", Name = "Result", InitialValueExpr = "string.Empty" }
+ },
+ };
+
string CSharpMethodCode { get; set; }
string CSharpClassCode { get; set; }
- IEnumerable<GlobalVar> GlobalVars { get; set; }
- IEnumerable<VSServiceRef> VSServiceRefs { get; set; }
-
CompilerResults CompilerResults { get; set; }
- Type Type { get; set; }
+ Assembly MacroAssembly { get; set; }
+ Type MacroClass { get; set; }
FieldInfo ResultField { get; set; }
Func<Task> Run { get; set; }
- readonly Assembly ExecutingAssembly = Assembly.GetExecutingAssembly();
const BindingFlags PUBLIC_STATIC = BindingFlags.Public | BindingFlags.Static;
- const string BR = "\r\n";
+ const StringComparison IGNORE_CASE = StringComparison.InvariantCultureIgnoreCase;
static ConcurrentDictionary<string, Macro> Macros
= new ConcurrentDictionary<string, Macro>();
-
/// <summary>
/// Macro constructor
/// </summary>
@@ -147,7 +183,7 @@ namespace QtVsTest.Macros
Package = package;
JoinableTaskFactory = joinableTaskFactory;
ServerLoop = serverLoop;
- Error("Uninitialized");
+ ErrorMsg("Uninitialized");
}
/// <summary>
@@ -178,7 +214,7 @@ namespace QtVsTest.Macros
return true;
} catch (Exception e) {
- return Error(e);
+ return ErrorException(e);
}
}
@@ -190,13 +226,16 @@ namespace QtVsTest.Macros
if (!Ok)
return;
+ if (string.IsNullOrEmpty(CSharpMethodCode))
+ return;
+
try {
InitGlobalVars();
await Run();
await SwitchToWorkerThreadAsync();
Result = ResultField.GetValue(null) as string;
} catch (Exception e) {
- Error(e);
+ ErrorException(e);
}
}
@@ -209,13 +248,13 @@ namespace QtVsTest.Macros
if (Parser == null) {
var parser = MacroParser.Get();
if (parser == null)
- return Error("Parser error");
+ return ErrorMsg("Parser error");
Parser = parser;
}
var macroLines = Parser.Parse(Message);
if (macroLines == null)
- return Error("Parse error");
+ return ErrorMsg("Parse error");
MacroLines = macroLines;
@@ -228,189 +267,36 @@ namespace QtVsTest.Macros
/// <returns></returns>
bool CompileMacro()
{
- const StringComparison IGNORE_CASE = StringComparison.InvariantCultureIgnoreCase;
- var selectedAssemblies = new List<string>(MSBuild.MetaInfo.QtVsTest.Reference)
- {
- ExecutingAssembly.FullName,
- "System.Core",
- "EnvDTE",
- "EnvDTE80",
- };
-
- var selectedNamespaces = new List<string>
- {
- "System",
- "Task = System.Threading.Tasks.Task",
- "System.Linq",
- "System.Reflection",
- "EnvDTE",
- "EnvDTE80",
- };
-
- string macroName = string.Empty;
- var serviceRefs = new Dictionary<string, VSServiceRef> {
- { "DTE", new VSServiceRef {
- Name = "DTE",
- Interface = "DTE2",
- Type = "DTE",
- }},
- };
- var globalVars = new Dictionary<string, GlobalVar>
- {
- { "Result", new GlobalVar {
- Type = "string",
- Name = "Result",
- InitialValueExpr = "string.Empty"
- }},
- };
- var csharpCode = new StringBuilder();
- bool quitWhenDone = false;
+ var csharp = new StringBuilder();
foreach (var line in MacroLines) {
- if (quitWhenDone)
- return Error("No code allowed after #quit");
+ if (QuitWhenDone)
+ return ErrorMsg("No code allowed after #quit");
if (line is CodeLine) {
var codeLine = line as CodeLine;
- csharpCode.Append(codeLine.Code);
+ csharp.Append(codeLine.Code + "\r\n");
continue;
}
- var s = line as Statement;
- switch (s.Type) {
-
- case StatementType.Quit:
- quitWhenDone = true;
- break;
-
- case StatementType.Macro:
- if (csharpCode.Length > 0)
- return Error("#macro must be first statement");
- if (!string.IsNullOrEmpty(macroName))
- return Error("Only one #macro statement allowed");
- if (s.Args.Count < 1)
- return Error("Missing macro name");
- macroName = s.Args[0];
- break;
-
- case StatementType.Thread:
- if (s.Args.Count < 1)
- return Error("Missing thread id");
- if (s.Args[0].Equals("ui", IGNORE_CASE))
- csharpCode.Append("await SwitchToUIThread();");
- else if (s.Args[0].Equals("default", IGNORE_CASE))
- csharpCode.Append("await SwitchToWorkerThread();");
- else
- return Error("Unknown thread id");
- break;
-
- case StatementType.Reference:
- if (!s.Args.Any())
- return Error("Missing args for #reference");
- selectedAssemblies.Add(s.Args.First());
- foreach (var ns in s.Args.Skip(1))
- selectedNamespaces.Add(ns);
- break;
-
- case StatementType.Using:
- if (!s.Args.Any())
- return Error("Missing args for #using");
- foreach (var ns in s.Args)
- selectedNamespaces.Add(ns);
- break;
-
- case StatementType.Var:
- if (s.Args.Count < 2)
- return Error("Missing args for #var");
- var typeName = s.Args[0];
- var varName = s.Args[1];
- var initValue = s.Code;
- if (varName.Where(c => char.IsWhiteSpace(c)).Any())
- return Error("Wrong var name");
- globalVars[varName] = new GlobalVar
- {
- Type = typeName,
- Name = varName,
- InitialValueExpr = initValue
- };
- break;
-
- case StatementType.Service:
- if (s.Args.Count <= 1)
- return Error("Missing args for #service");
- var serviceVarName = s.Args[0];
- if (serviceVarName.Where(c => char.IsWhiteSpace(c)).Any())
- return Error("Wrong service var name");
- if (serviceRefs.ContainsKey(serviceVarName))
- return Error("Duplicate service var name");
- serviceRefs.Add(serviceVarName, new VSServiceRef
- {
- Name = serviceVarName,
- Interface = s.Args[1],
- Type = s.Args.Count > 2 ? s.Args[2] : s.Args[1]
- });
- break;
-
- case StatementType.Call:
- if (s.Args.Count < 1)
- return Error("Missing args for #call");
- var calleeName = s.Args[0];
- var callee = GetMacro(calleeName);
- if (callee == null)
- return Error("Undefined macro");
- csharpCode.AppendFormat("await CallMacro(\"{0}\");", calleeName);
- foreach (var globalVar in callee.GlobalVars) {
- if (globalVars.ContainsKey(globalVar.Name))
- continue;
- globalVars[globalVar.Name] = new GlobalVar
- {
- Type = globalVar.Type,
- Name = globalVar.Name
- };
- }
- break;
-
- case StatementType.Wait:
- if (string.IsNullOrEmpty(s.Code))
- return Error("Missing args for #wait");
- var expr = s.Code;
- uint timeout = uint.MaxValue;
- if (s.Args.Count > 0 && !uint.TryParse(s.Args[0], out timeout))
- return Error("Timeout format error in #wait");
- if (s.Args.Count > 2) {
- var evalVarType = s.Args[1];
- var evalVarName = s.Args[2];
- csharpCode.AppendFormat(
- "{0} {1} = default({0});" +
- "await WaitExpr({2}, () => {1} = {3});",
- evalVarType, evalVarName, timeout, expr);
- } else {
- csharpCode.AppendFormat(
- "await WaitExpr({0}, () => {1});",
- timeout, expr);
- }
- break;
- }
+ if (!GenerateStatement(line as Statement, csharp))
+ return false;
}
- if (csharpCode.Length > 0)
- CSharpMethodCode = csharpCode.ToString();
+ if (csharp.Length > 0)
+ CSharpMethodCode = csharp.ToString();
- if (string.IsNullOrEmpty(macroName))
+ AutoRun = string.IsNullOrEmpty(Name);
+ if (AutoRun)
Name = "Macro_" + Path.GetRandomFileName().Replace(".", "");
- else if (!SaveMacro(macroName))
- return Error("Macro already defined");
+ else if (!SaveMacro(Name))
+ return ErrorMsg("Macro already defined");
- GlobalVars = globalVars.Values;
- VSServiceRefs = serviceRefs.Values;
- foreach (var sv in VSServiceRefs.Where(x => string.IsNullOrEmpty(x.Type)))
+ foreach (var sv in ServiceRefs.Values.Where(x => string.IsNullOrEmpty(x.Type)))
sv.Type = sv.Interface;
- AutoRun = string.IsNullOrEmpty(macroName);
- QuitWhenDone = quitWhenDone;
-
- var selectedAssemblyNames = selectedAssemblies
+ var selectedAssemblyNames = SelectedAssemblies
.Select(x => new AssemblyName(x))
.GroupBy(x => x.FullName)
.Select(x => x.First());
@@ -437,52 +323,249 @@ namespace QtVsTest.Macros
.Where(x => x != null)
.Select(x => x.Location);
- RefNamespaces = selectedNamespaces;
-
return NoError();
}
+ bool GenerateStatement(Statement s, StringBuilder csharp)
+ {
+ switch (s.Type) {
+
+ case StatementType.Quit:
+ QuitWhenDone = true;
+ break;
+
+ case StatementType.Macro:
+ if (csharp.Length > 0)
+ return ErrorMsg("#macro must be first statement");
+ if (!string.IsNullOrEmpty(Name))
+ return ErrorMsg("Only one #macro statement allowed");
+ if (s.Args.Count < 1)
+ return ErrorMsg("Missing macro name");
+ Name = s.Args[0];
+ break;
+
+ case StatementType.Thread:
+ if (s.Args.Count < 1)
+ return ErrorMsg("Missing thread id");
+ if (s.Args[0].Equals("ui", IGNORE_CASE)) {
+
+ csharp.Append(
+/** BEGIN generate code **/
+@"
+ await SwitchToUIThread();"
+/** END generate code **/ );
+
+ } else if (s.Args[0].Equals("default", IGNORE_CASE)) {
+
+ csharp.Append(
+/** BEGIN generate code **/
+@"
+ await SwitchToWorkerThread();"
+/** END generate code **/ );
+
+ } else {
+ return ErrorMsg("Unknown thread id");
+ }
+ break;
+
+ case StatementType.Reference:
+ if (!s.Args.Any())
+ return ErrorMsg("Missing args for #reference");
+ SelectedAssemblies.Add(s.Args.First());
+ foreach (var ns in s.Args.Skip(1))
+ Namespaces.Add(ns);
+ break;
+
+ case StatementType.Using:
+ if (!s.Args.Any())
+ return ErrorMsg("Missing args for #using");
+ foreach (var ns in s.Args)
+ Namespaces.Add(ns);
+ break;
+
+ case StatementType.Var:
+ if (s.Args.Count < 2)
+ return ErrorMsg("Missing args for #var");
+ var typeName = s.Args[0];
+ var varName = s.Args[1];
+ var initValue = s.Code;
+ if (varName.Where(c => char.IsWhiteSpace(c)).Any())
+ return ErrorMsg("Wrong var name");
+ GlobalVars[varName] = new GlobalVar
+ {
+ Type = typeName,
+ Name = varName,
+ InitialValueExpr = initValue
+ };
+ break;
+
+ case StatementType.Service:
+ if (s.Args.Count <= 1)
+ return ErrorMsg("Missing args for #service");
+ var serviceVarName = s.Args[0];
+ if (serviceVarName.Where(c => char.IsWhiteSpace(c)).Any())
+ return ErrorMsg("Invalid service var name");
+ if (ServiceRefs.ContainsKey(serviceVarName))
+ return ErrorMsg("Duplicate service var name");
+ ServiceRefs.Add(serviceVarName, new VSServiceRef
+ {
+ Name = serviceVarName,
+ Interface = s.Args[1],
+ Type = s.Args.Count > 2 ? s.Args[2] : s.Args[1]
+ });
+ break;
+
+ case StatementType.Call:
+ if (s.Args.Count < 1)
+ return ErrorMsg("Missing args for #call");
+ var calleeName = s.Args[0];
+ var callee = GetMacro(calleeName);
+ if (callee == null)
+ return ErrorMsg("Undefined macro");
+
+ csharp.AppendFormat(
+/** BEGIN generate code **/
+@"
+ await CallMacro(""{0}"");"
+/** END generate code **/ , calleeName);
+
+ foreach (var globalVar in callee.GlobalVars.Values) {
+ if (GlobalVars.ContainsKey(globalVar.Name))
+ continue;
+ GlobalVars[globalVar.Name] = new GlobalVar
+ {
+ Type = globalVar.Type,
+ Name = globalVar.Name
+ };
+ }
+ break;
+
+ case StatementType.Wait:
+ if (string.IsNullOrEmpty(s.Code))
+ return ErrorMsg("Missing args for #wait");
+ var expr = s.Code;
+ uint timeout = uint.MaxValue;
+ if (s.Args.Count > 0 && !uint.TryParse(s.Args[0], out timeout))
+ return ErrorMsg("Timeout format error in #wait");
+ if (s.Args.Count > 2) {
+ var evalVarType = s.Args[1];
+ var evalVarName = s.Args[2];
+
+ csharp.AppendFormat(
+/** BEGIN generate code **/
+@"
+ {0} {1} = default({0});
+ await WaitExpr({2}, () => {1} = {3});"
+/** END generate code **/ , evalVarType,
+ evalVarName,
+ timeout,
+ expr);
+
+ } else {
+
+ csharp.AppendFormat(
+/** BEGIN generate code **/
+@"
+ await WaitExpr({0}, () => {1});"
+/** END generate code **/ , timeout,
+ expr);
+
+ }
+ break;
+ }
+ return true;
+ }
+
+ const string SERVICETYPE_PREFIX = "_ServiceType_";
+ const string INIT_PREFIX = "_Init_";
+ string MethodName { get { return string.Format("_Run_{0}_Async", Name); } }
+
+ bool GenerateClass()
+ {
+ var csharp = new StringBuilder();
+ foreach (var ns in Namespaces) {
+ csharp.AppendFormat(
+/** BEGIN generate code **/
+@"
+using {0};"
+/** END generate code **/ , ns);
+ }
+
+ csharp.AppendFormat(
+/** BEGIN generate code **/
+@"
+namespace QtVsTest.Macros
+{{
+ public class {0}
+ {{"
+/** END generate code **/ , Name);
+
+ foreach (var serviceRef in ServiceRefs.Values) {
+ csharp.AppendFormat(
+/** BEGIN generate code **/
+@"
+ public static {2} {1};
+ public static readonly Type {0}{1} = typeof({3});"
+/** END generate code **/ , SERVICETYPE_PREFIX,
+ serviceRef.Name,
+ serviceRef.Interface,
+ serviceRef.Type);
+ }
+
+ foreach (var globalVar in GlobalVars.Values) {
+ csharp.AppendFormat(
+/** BEGIN generate code **/
+@"
+ public static {1} {2};
+ public static {1} {0}{2} {{ get {{ return ({3}); }} }}"
+/** END generate code **/ , INIT_PREFIX,
+ globalVar.Type,
+ globalVar.Name,
+ globalVar.InitialValueExpr);
+ }
+
+ csharp.Append(
+/** BEGIN generate code **/
+@"
+ public static Func<string, Assembly> GetAssembly;
+ public static Func<Task> SwitchToUIThread;
+ public static Func<Task> SwitchToWorkerThread;
+ public static Func<string, Task> CallMacro;
+ public static Func<int, Func<object>, Task> WaitExpr;"
+/** END generate code **/ );
+
+ if (!GenerateResultFuncs(csharp))
+ return false;
+
+ csharp.AppendFormat(
+/** BEGIN generate code **/
+@"
+ public static async Task {0}()
+ {{
+{1}
+ }}
+
+ }} /*class*/
+
+}} /*namespace*/"
+/** END generate code **/ , MethodName,
+ CSharpMethodCode);
+
+ CSharpClassCode = csharp.ToString();
+
+ return true;
+ }
+
/// <summary>
/// Generate and compile C# class for macro
/// </summary>
/// <returns></returns>
bool CompileClass()
{
- var methodName = string.Format("_Run_{0}_Async", Name);
- const string serviceTypePrefix = "_ServiceType_";
- const string initPrefix = "_Init_";
-
- var csharpClass = new StringBuilder();
- foreach (var ns in RefNamespaces)
- csharpClass.AppendFormat("using {0};", ns);
- csharpClass.Append("namespace QtVsTest.Macros");
- csharpClass.Append("{");
- csharpClass.AppendFormat("public class {0}", Name);
- csharpClass.Append("{");
- foreach (var serviceRef in VSServiceRefs) {
- csharpClass.AppendFormat("public static {0} {1};",
- serviceRef.Interface, serviceRef.Name);
- csharpClass.AppendFormat("public static readonly Type {0}{1} = typeof({2});",
- serviceTypePrefix, serviceRef.Name, serviceRef.Type);
- }
- foreach (var globalVar in GlobalVars) {
- csharpClass.AppendFormat("public static {0} {1};", globalVar.Type, globalVar.Name);
- csharpClass.AppendFormat(
- "public static {0} {1}{2} {{ get {{ return ({3}); }} }}",
- globalVar.Type, initPrefix, globalVar.Name, globalVar.InitialValueExpr);
- }
- csharpClass.Append("public static Func<string, Assembly> GetAssembly;");
- csharpClass.Append("public static Func<Task> SwitchToUIThread;");
- csharpClass.Append("public static Func<Task> SwitchToWorkerThread;");
- csharpClass.Append("public static Func<string, Task> CallMacro;");
- csharpClass.Append("public static Func<int, Func<object>, Task> WaitExpr;");
- csharpClass.AppendFormat("public static async Task {0}()", methodName);
- csharpClass.Append("{" + CSharpMethodCode + "}");
- csharpClass.Append("} /*class*/ } /*namespace*/");
-
- CSharpClassCode = csharpClass.ToString();
-
- var dllUri = new Uri(ExecutingAssembly.EscapedCodeBase);
+ if (!GenerateClass())
+ return false;
+
+ var dllUri = new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase);
var dllPath = Uri.UnescapeDataString(dllUri.AbsolutePath);
var macroDllPath = Path.Combine(Path.GetDirectoryName(dllPath), Name + ".dll");
@@ -502,50 +585,66 @@ namespace QtVsTest.Macros
if (CompilerResults.Errors.Count > 0) {
if (File.Exists(macroDllPath))
File.Delete(macroDllPath);
- return Error(string.Join(BR,
+ return ErrorMsg(string.Join("\r\n",
CompilerResults.Errors.Cast<CompilerError>()
.Select(x => x.ErrorText)));
}
- var assembly = AppDomain.CurrentDomain.Load(File.ReadAllBytes(macroDllPath));
- var type = assembly.GetType(string.Format("QtVsTest.Macros.{0}", Name));
+ MacroAssembly = AppDomain.CurrentDomain.Load(File.ReadAllBytes(macroDllPath));
+ MacroClass = MacroAssembly.GetType(string.Format("QtVsTest.Macros.{0}", Name));
+ AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
if (File.Exists(macroDllPath))
File.Delete(macroDllPath);
- foreach (var serviceVar in VSServiceRefs) {
- serviceVar.RefVar = type.GetField(serviceVar.Name, PUBLIC_STATIC);
- var serviceType = type.GetField(serviceTypePrefix + serviceVar.Name, PUBLIC_STATIC);
+ foreach (var serviceVar in ServiceRefs.Values) {
+ serviceVar.RefVar = MacroClass.GetField(serviceVar.Name, PUBLIC_STATIC);
+ var serviceType = MacroClass.GetField(SERVICETYPE_PREFIX + serviceVar.Name, PUBLIC_STATIC);
serviceVar.ServiceType = (Type)serviceType.GetValue(null);
}
- ResultField = type.GetField("Result", PUBLIC_STATIC);
- foreach (var globalVar in GlobalVars) {
- globalVar.FieldInfo = type.GetField(globalVar.Name, PUBLIC_STATIC);
- globalVar.InitInfo = type.GetProperty(initPrefix + globalVar.Name, PUBLIC_STATIC);
+ ResultField = MacroClass.GetField("Result", PUBLIC_STATIC);
+ foreach (var globalVar in GlobalVars.Values) {
+ globalVar.FieldInfo = MacroClass.GetField(globalVar.Name, PUBLIC_STATIC);
+ globalVar.InitInfo = MacroClass.GetProperty(INIT_PREFIX + globalVar.Name, PUBLIC_STATIC);
}
Run = (Func<Task>)Delegate.CreateDelegate(typeof(Func<Task>),
- type.GetMethod(methodName, PUBLIC_STATIC));
+ MacroClass.GetMethod(MethodName, PUBLIC_STATIC));
- type.GetField("GetAssembly", PUBLIC_STATIC)
+ MacroClass.GetField("GetAssembly", PUBLIC_STATIC)
.SetValue(null, new Func<string, Assembly>(GetAssembly));
- type.GetField("SwitchToUIThread", PUBLIC_STATIC)
+ MacroClass.GetField("SwitchToUIThread", PUBLIC_STATIC)
.SetValue(null, new Func<Task>(SwitchToUIThreadAsync));
- type.GetField("SwitchToWorkerThread", PUBLIC_STATIC)
+ MacroClass.GetField("SwitchToWorkerThread", PUBLIC_STATIC)
.SetValue(null, new Func<Task>(SwitchToWorkerThreadAsync));
- type.GetField("CallMacro", PUBLIC_STATIC)
+ MacroClass.GetField("CallMacro", PUBLIC_STATIC)
.SetValue(null, new Func<string, Task>(CallMacroAsync));
- type.GetField("WaitExpr", PUBLIC_STATIC)
+ MacroClass.GetField("WaitExpr", PUBLIC_STATIC)
.SetValue(null, new Func<int, Func<object>, Task>(WaitExprAsync));
return NoError();
}
+ Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
+ {
+ if (args.RequestingAssembly == null || args.RequestingAssembly != MacroAssembly)
+ return null;
+ var fullName = new AssemblyName(args.Name);
+ var assemblyPath = RefAssemblies
+ .Where(x => Path.GetFileNameWithoutExtension(x).Equals(fullName.Name, IGNORE_CASE))
+ .FirstOrDefault();
+ if (string.IsNullOrEmpty(assemblyPath))
+ return null;
+ if (!File.Exists(assemblyPath))
+ return null;
+ return Assembly.LoadFrom(assemblyPath);
+ }
+
public static Assembly GetAssembly(string name)
{
return AppDomain.CurrentDomain.GetAssemblies()
@@ -580,12 +679,21 @@ namespace QtVsTest.Macros
var tMax = TimeSpan.FromMilliseconds(timeout);
var tRemaining = tMax;
var t = Stopwatch.StartNew();
- object value = await Task.Run(() => expr()).WithTimeout(tRemaining);
+ object value;
+ try {
+ value = await Task.Run(() => expr()).WithTimeout(tRemaining);
+ } catch {
+ value = null;
+ }
bool ok = !IsDefaultValue(value);
while (!ok && (tRemaining = (tMax - t.Elapsed)) > TimeSpan.Zero) {
await Task.Delay(10);
- value = await Task.Run(() => expr()).WithTimeout(tRemaining);
+ try {
+ value = await Task.Run(() => expr()).WithTimeout(tRemaining);
+ } catch {
+ value = null;
+ }
ok = !IsDefaultValue(value);
}
@@ -598,14 +706,14 @@ namespace QtVsTest.Macros
if (obj == null)
return true;
else if (obj.GetType().IsValueType)
- return obj == Activator.CreateInstance(obj.GetType());
+ return obj.Equals(Activator.CreateInstance(obj.GetType()));
else
return false;
}
void InitGlobalVars()
{
- var globalVarsInit = GlobalVars
+ var globalVarsInit = GlobalVars.Values
.Where(x => x.FieldInfo != null && !string.IsNullOrEmpty(x.InitialValueExpr));
foreach (var globalVar in globalVarsInit)
globalVar.FieldInfo.SetValue(null, globalVar.InitInfo.GetValue(null));
@@ -613,8 +721,8 @@ namespace QtVsTest.Macros
void CopyGlobalVarsFrom(Macro src)
{
- var globalVars = GlobalVars
- .Join(src.GlobalVars,
+ var globalVars = GlobalVars.Values
+ .Join(src.GlobalVars.Values,
DstVar => DstVar.Name, SrcVar => SrcVar.Name,
(DstVar, SrcVar) => new { DstVar, SrcVar })
.Where(x => x.SrcVar.FieldInfo != null && x.DstVar.FieldInfo != null
@@ -629,7 +737,7 @@ namespace QtVsTest.Macros
async Task<bool> GetServicesAsync()
{
- foreach (var serviceRef in VSServiceRefs.Where(x => x.RefVar != null)) {
+ foreach (var serviceRef in ServiceRefs.Values.Where(x => x.RefVar != null)) {
serviceRef.RefVar.SetValue(null,
await Package.GetServiceAsync(serviceRef.ServiceType));
}
@@ -651,35 +759,69 @@ namespace QtVsTest.Macros
return macro;
}
+ bool GenerateResultFuncs(StringBuilder csharp)
+ {
+ csharp.Append(
+/** BEGIN generate code **/
+@"
+ public static string Ok;
+ public static string Error;
+ public static Func<string, string> ErrorMsg;"
+/** END generate code **/ );
+ return true;
+ }
+
+ bool InitializeResultFuncs()
+ {
+ if (MacroClass == null)
+ return false;
+
+ MacroClass.GetField("Ok", PUBLIC_STATIC)
+ .SetValue(null, MACRO_OK);
+
+ MacroClass.GetField("Error", PUBLIC_STATIC)
+ .SetValue(null, MACRO_ERROR);
+
+ MacroClass.GetField("ErrorMsg", PUBLIC_STATIC)
+ .SetValue(null, new Func<string, string>(MACRO_ERROR_MSG));
+
+ return true;
+ }
+
+ string MACRO_OK { get { return "(ok)"; } }
+ string MACRO_ERROR { get { return "(error)"; } }
+ string MACRO_WARN { get { return "(warn)"; } }
+ string MACRO_ERROR_MSG(string msg) { return string.Format("{0}\r\n{1}", MACRO_ERROR, msg); }
+ string MACRO_WARN_MSG(string msg) { return string.Format("{0}\r\n{1}", MACRO_WARN, msg); }
+
bool NoError()
{
- Result = "(ok)";
+ Result = MACRO_OK;
return (Ok = true);
}
bool Error()
{
- Result = "(error)";
+ Result = MACRO_ERROR;
return (Ok = false);
}
- bool Error(string errorMsg)
+ bool ErrorMsg(string errorMsg)
{
- Result = "(error)" + BR + errorMsg;
+ Result = MACRO_ERROR_MSG(errorMsg);
return (Ok = false);
}
- bool Error(Exception e)
+ bool ErrorException(Exception e)
{
- Result = string.Format("(error)" + BR +
- "{0}" + BR + "\"{1}\"" + BR + "{2}",
- e.GetType().Name, e.Message, e.StackTrace);
+ Result = MACRO_ERROR_MSG(string.Format("{0}\r\n\"{1}\"\r\n{2}",
+ e.GetType().Name, e.Message, e.StackTrace));
return (Ok = false);
}
bool Warning(string warnMsg)
{
- Result = "(warn)" + BR + warnMsg;
+ Result = MACRO_WARN_MSG(warnMsg);
return (Ok = true);
}
}
diff --git a/src/qtvstest/MacroClient.h b/src/qtvstest/MacroClient.h
index 0b709b46..f8f6589b 100644
--- a/src/qtvstest/MacroClient.h
+++ b/src/qtvstest/MacroClient.h
@@ -29,10 +29,27 @@
#ifndef MACROCLIENT_H
#define MACROCLIENT_H
+#include <QString>
#include <QElapsedTimer>
#include <QtNetwork>
#include <QProcess>
+#define MACRO_OK QStringLiteral("(ok)")
+#define MACRO_ERROR QStringLiteral("(error)")
+#define MACRO_ERROR_MSG(msg) QStringLiteral("(error)\r\n" msg)
+
+#define MACRO_ASSERT_OK(result) QCOMPARE(result, MACRO_OK)
+
+inline bool macroResultOk(QString result)
+{
+ return result == MACRO_OK;
+}
+
+inline bool macroResultError(QString result)
+{
+ return result.startsWith(MACRO_ERROR);
+}
+
class MacroClient
{
@@ -108,7 +125,7 @@ public:
QString runMacro(QString macroCode)
{
if (socket.state() != QLocalSocket::ConnectedState && !connectToServer())
- return QStringLiteral("(error)\r\nDisconnected");
+ return MACRO_ERROR_MSG("Disconnected");
QByteArray data = macroCode.toUtf8();
int size = data.size();
@@ -118,24 +135,24 @@ public:
socket.flush();
if (socket.state() != QLocalSocket::ConnectedState)
- return QStringLiteral("(error)\r\nDisconnected");
+ return MACRO_ERROR_MSG("Disconnected");
while (socket.state() == QLocalSocket::ConnectedState && socket.bytesToWrite())
socket.waitForBytesWritten(15000);
if (socket.state() != QLocalSocket::ConnectedState)
- return QStringLiteral("(error)\r\nDisconnected");
+ return MACRO_ERROR_MSG("Disconnected");
while (socket.state() == QLocalSocket::ConnectedState && socket.bytesAvailable() < 4)
socket.waitForReadyRead(15000);
if (socket.state() != QLocalSocket::ConnectedState)
- return QStringLiteral("(error)\r\nDisconnected");
+ return MACRO_ERROR_MSG("Disconnected");
size = *reinterpret_cast<int *>(socket.read(4).data());
while (socket.state() == QLocalSocket::ConnectedState && socket.bytesAvailable() < size)
socket.waitForReadyRead(15000);
if (socket.state() != QLocalSocket::ConnectedState)
- return QStringLiteral("(error)\r\nDisconnected");
+ return MACRO_ERROR_MSG("Disconnected");
data = socket.read(size);
return QString::fromUtf8(data);