using AddInPlugin.Entity;
|
using OpenTap;
|
using OpenTap.Addin.Annotation;
|
using OpenTap.Addin.Util;
|
using System;
|
using System.Collections.Concurrent;
|
using System.Collections.Generic;
|
using System.Collections.ObjectModel;
|
using System.IO;
|
using System.Linq;
|
using System.Reflection;
|
using System.Xml.Serialization;
|
using UtilLib;
|
|
namespace AddInPlugin
|
{
|
public class DllData
|
{
|
public DateTime lastUpdateTime { get; set; }
|
|
public Assembly assembly { get; set; }
|
|
public ObservableCollection<string> types { get; set; }
|
|
public DllData(DateTime lastUpdateTime, string[] types)
|
{
|
this.lastUpdateTime = lastUpdateTime;
|
this.types = new ObservableCollection<string>(types);
|
}
|
}
|
|
public class DllLoader
|
{
|
|
private DllLoader()
|
{
|
}
|
|
private static object lockObj = new object();
|
|
private ConcurrentDictionary<string, DllData> loadedCache = new ConcurrentDictionary<string, DllData>();
|
private ConcurrentDictionary<Type, List<MethodInfo>> loadedMethod = new ConcurrentDictionary<Type, List<MethodInfo>>();
|
|
private static DllLoader instance;
|
public static DllLoader Instance
|
{
|
get
|
{
|
lock (lockObj)
|
{
|
if (instance == null)
|
{
|
instance = new DllLoader();
|
}
|
return instance;
|
}
|
}
|
}
|
|
public Assembly LoadAssembly(string path, out ObservableCollection<string> typeNames)
|
{
|
path = path?.ToLower();
|
typeNames = null;
|
if (!File.Exists(path))
|
{
|
return null;
|
}
|
DateTime newTime = File.GetLastWriteTime(path);
|
if (!loadedCache.ContainsKey(path) || loadedCache[path].lastUpdateTime != newTime)
|
{
|
var assembly = Assembly.LoadFrom(path);
|
var types = assembly.GetTypes();
|
|
var data = new DllData(newTime, types.Select(e => e.ToString()).ToArray())
|
{
|
assembly = assembly,
|
};
|
loadedCache[path] = data;
|
typeNames = data.types;
|
}
|
typeNames = loadedCache[path].types;
|
return loadedCache[path].assembly;
|
}
|
|
//public List<MethodInfo> LoadMethods(Type type) {
|
// if (!loadedMethod.ContainsKey(type)) {
|
// loadedMethod[type] = MethodInfoHelper.GetPublicMethodNames(type);
|
// }
|
//}
|
}
|
|
public enum VariableDirection
|
{
|
In = 0,
|
Out = 1
|
}
|
|
public class MethodVariable : TapNotifyBase
|
{
|
private string name;
|
[DataGridCol("Ãû³Æ", "Auto", true)]
|
public string Name
|
{
|
get => name;
|
set
|
{
|
Set(ref name, value);
|
}
|
}
|
|
[XmlIgnore]
|
public Type Type { get; set; }
|
|
[XmlIgnore]
|
public object Value { get; set; }
|
|
private VariableDirection direction;
|
[DataGridCol("·½Ïò", "Auto", true)]
|
public VariableDirection Direction
|
{
|
get => direction;
|
set
|
{
|
Set(ref direction, value);
|
}
|
}
|
|
private string typeName;
|
[DataGridCol("ÀàÐÍ", "Auto", true)]
|
public string TypeName
|
{
|
get => typeName;
|
set
|
{
|
Set(ref typeName, value);
|
}
|
}
|
|
private bool log;
|
[DataGridCol("Log", "Auto", false)]
|
public bool Log
|
{
|
get => log;
|
set
|
{
|
Set(ref log, value);
|
}
|
}
|
|
private string valueStr;
|
[DataGridCol("Öµ", "*", false, true)]
|
public string ValueStr
|
{
|
get => valueStr;
|
set
|
{
|
Set(ref valueStr, value);
|
}
|
}
|
}
|
|
[Prototype]
|
[Display("DotNetStep", Description: "ÔËÐÐ.Net")]
|
|
public class DotNetStep : VariableTestStep
|
{
|
#region Settings
|
|
private string dllPath;
|
|
[Obsolete]
|
//[Display("·¾¶", Order: 0.0, Description: "Dll¾ø¶Ô·¾¶.")]
|
[FilePath(FilePathAttribute.BehaviorChoice.Open)]
|
public string DllPath
|
{
|
get => null;
|
set
|
{
|
if (dllPath != value)
|
{
|
Set(ref dllPath, value);
|
// LoadDll();
|
}
|
}
|
}
|
|
public bool ShouldSerializeDllPath()
|
{
|
return false;
|
}
|
|
|
private LocationString dllLocation = new LocationString();
|
[Display("·¾¶", Order: 0.0, Description: "Dll¾ø¶Ô·¾¶.")]
|
[FilePath(FilePathAttribute.BehaviorChoice.Open, fileExtension: "dll|*.dll")]
|
public LocationString DllLocation
|
{
|
get => dllLocation;
|
set
|
{
|
Set(ref dllLocation, value);
|
}
|
}
|
|
private Assembly assembly;
|
|
private Type targetType;
|
|
private ObservableCollection<string> types;
|
[XmlIgnore]
|
public ObservableCollection<string> Types
|
{
|
get => types;
|
set
|
{
|
types = value;
|
OnPropertyChanged(nameof(Types));
|
}
|
}
|
|
private string className;
|
[Display("ˈ̞", Order: 0.0, Description: "ˈ̞.")]
|
[DynamicSelect("Types")]
|
public string ClassName
|
{
|
get => className;
|
set
|
{
|
if (loadException == null)
|
{
|
Set(ref className, value);
|
if (!string.IsNullOrEmpty(value))
|
{
|
LoadType();
|
}
|
}
|
}
|
}
|
|
private ObservableCollection<string> methods;
|
[XmlIgnore]
|
public ObservableCollection<string> Methods
|
{
|
get => methods;
|
set
|
{
|
Set(ref methods, value);
|
}
|
}
|
|
private string methodName;
|
[Display("·½·¨", Order: 0.0, Description: "·½·¨Ãû.")]
|
//[DynamicSelect(nameof(MethodName), "ClassName", "RefreshMethodName")]
|
[DynamicSelect("Methods")]
|
public string MethodName
|
{
|
get => methodName;
|
set
|
{
|
if (loadException == null)
|
{
|
Set(ref methodName, value);
|
if (!string.IsNullOrEmpty(value))
|
{
|
RefreshVariables();
|
}
|
}
|
}
|
}
|
|
private Dictionary<string, MethodBase> methodCache = new Dictionary<string, MethodBase>();
|
|
[XmlIgnore]
|
private object instance;
|
|
#endregion
|
|
|
private Exception loadException;
|
|
[XmlIgnore]
|
public Exception LoadException
|
{
|
get => loadException;
|
set
|
{
|
Set(ref loadException, value);
|
}
|
}
|
|
public DotNetStep()
|
{
|
// ToDo: Set default values for properties / settings.
|
//Rules.Add(() => !string.IsNullOrWhiteSpace(DllPath), "·¾¶²»ÄÜΪ¿Õ", DllPath);
|
//Rules.Add(() => DllLocation.GetLocation(Root.Path, out _), "·¾¶²»´æÔÚ", nameof(DllLocation));
|
Rules.Add(() => DllLocation.GetLocation("C:\\Users\\chrry\\Desktop\\Release\\Api\\test.plan", out _), "·¾¶²»´æÔÚ", nameof(DllLocation));
|
Rules.Add(() => !string.IsNullOrWhiteSpace(ClassName), "ÀàÃû²»ÄÜΪ¿Õ", nameof(ClassName));
|
Rules.Add(() => LoadException == null, () => $"Dll¼ÓÔØÊ§°Ü:{LoadException}", nameof(DllLocation));
|
}
|
|
private void DllLocation_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
{
|
LoadDll();
|
}
|
|
protected override void OnRootChanged(TestPlan old, TestPlan plan)
|
{
|
try
|
{
|
if (!string.IsNullOrEmpty(dllPath))
|
{
|
DllLocation.AbsoluteLocation = dllPath;
|
dllPath = null;
|
}
|
DllLocation.Refresh(Root?.Path);
|
DllLocation.PropertyChanged += DllLocation_PropertyChanged;
|
|
if (!DllLocation.GetLocation(Root.Path, out string path))
|
{
|
throw new Exception("Dll Exception");
|
}
|
assembly = Assembly.LoadFrom(path);
|
Types = new ObservableCollection<string>(assembly.GetTypes().Select(e => e.ToString()).ToArray());
|
LoadType();
|
RefreshVariables();
|
LoadException = null;
|
}
|
catch (Exception ex)
|
{
|
LoadException = ex;
|
//Log.Error("DotNetStep load dll error. {0}", ex);
|
}
|
}
|
|
//private void LoadDll()
|
//{
|
// try
|
// {
|
// if (string.IsNullOrEmpty(DllPath))
|
// {
|
// return;
|
// }
|
// assembly = Assembly.LoadFrom(DllPath);
|
// Types = new ObservableCollection<string>(assembly.GetTypes().Select(e => e.ToString()).ToArray());
|
// LoadException = null;
|
// }
|
// catch (Exception ex)
|
// {
|
// LoadException = ex;
|
// //Log.Error("DotNetStep load dll error. {0}", ex);
|
// }
|
//}
|
|
private void LoadDll()
|
{
|
try
|
{
|
if (!DllLocation.GetLocation(Root?.Path, out string path))
|
{
|
throw new Exception("Dll Exception");
|
}
|
assembly = DllLoader.Instance.LoadAssembly(path, out var types);
|
Types = types;
|
LoadException = null;
|
}
|
catch (Exception ex)
|
{
|
LoadException = ex;
|
//Log.Error("DotNetStep load dll error. {0}", ex);
|
}
|
}
|
|
public void LoadType()
|
{
|
methodCache.Clear();
|
try
|
{
|
targetType = assembly?.GetType(ClassName);
|
if (targetType != null)
|
{
|
var creators = targetType?.GetConstructors();
|
if (creators != null)
|
{
|
for (int i = 0; i < creators.Length; i++)
|
{
|
var creator = creators[i];
|
var ps = creator.GetParameters();
|
var displayName = $"{targetType.Name} ({string.Join(", ", ps.Select(p => $"{p.ParameterType.ToString()} {p.Name}").ToArray())})";
|
methodCache[displayName] = creator;
|
}
|
}
|
|
var methods = MethodInfoHelper.GetPublicMethodNames(targetType);
|
if (methods != null)
|
{
|
for (int i = 0; i < methods.Count; i++)
|
{
|
var method = methods[i];
|
var ps = method.GetParameters();
|
var displayName = $"{method.Name} ({string.Join(", ", ps.Select(p => $"{p.ParameterType.ToString()} {p.Name}").ToArray())})";
|
methodCache[displayName] = method;
|
}
|
}
|
}
|
Methods = new ObservableCollection<string>(methodCache.Keys.ToArray());
|
}
|
catch (Exception ex)
|
{
|
Log.Error("DotNetStep LoadTypes error. {0}", ex);
|
}
|
}
|
|
private void ConcatVariable(MethodVariable mv, MethodVariable[] source)
|
{
|
MethodVariables.Add(mv);
|
var preview = source?.FirstOrDefault(v => v.Name == mv.Name && !string.IsNullOrEmpty(v.ValueStr));
|
if (preview != null)
|
{
|
mv.ValueStr = preview.ValueStr;
|
}
|
}
|
|
public void RefreshVariables()
|
{
|
if (MethodVariables == null)
|
{
|
MethodVariables = new ObservableCollection<MethodVariable>();
|
}
|
var last = MethodVariables?.ToArray();
|
MethodVariables.Clear();
|
try
|
{
|
if (!methodCache.ContainsKey(MethodName))
|
{
|
return;
|
}
|
var method = methodCache[MethodName];
|
var ps = method.GetParameters();
|
var IsStatic = method.IsStatic;
|
var IsConstructorMethod = method.IsConstructor;
|
|
if (IsConstructorMethod)
|
{
|
var newV = new MethodVariable
|
{
|
Name = "(return)",
|
Type = targetType,
|
TypeName = targetType?.ToString(),
|
Direction = VariableDirection.Out,
|
};
|
ConcatVariable(newV, last);
|
}
|
else if (!IsStatic)
|
{
|
var mi = (MethodInfo)method;
|
var newV = new MethodVariable
|
{
|
Name = "(instance)",
|
Type = targetType,
|
TypeName = targetType?.ToString(),
|
Direction = VariableDirection.In,
|
};
|
ConcatVariable(newV, last);
|
newV = new MethodVariable
|
{
|
Name = "(return)",
|
Type = mi.ReturnType,
|
TypeName = mi.ReturnType?.ToString(),
|
Direction = VariableDirection.Out,
|
};
|
ConcatVariable(newV, last);
|
}
|
else
|
{
|
var mi = (MethodInfo)method;
|
|
ConcatVariable(new MethodVariable
|
{
|
Name = "(return)",
|
Type = mi.ReturnType,
|
TypeName = mi.ReturnType?.ToString(),
|
Direction = VariableDirection.Out,
|
}, last);
|
}
|
|
foreach (var p in ps)
|
{
|
ConcatVariable(new MethodVariable
|
{
|
Name = p.Name,
|
Type = p.ParameterType,
|
TypeName = p.ParameterType.ToString(),
|
Direction = p.IsOut ? VariableDirection.Out : VariableDirection.In,
|
}, last);
|
}
|
// MethodVariables = variables;
|
}
|
catch (Exception ex)
|
{
|
Log.Error("DotNetStep LoadVariables error. {0}", ex);
|
}
|
}
|
|
public override void PrePlanRun()
|
{
|
// ToDo: Optionally add any setup code this step needs to run before the testplan starts
|
base.PrePlanRun();
|
}
|
|
/// <summary>
|
/// ת»»²ÎÊý
|
/// </summary>
|
private void ResolveVariables()
|
{
|
var method = methodCache[methodName];
|
var IsStatic = method.IsStatic;
|
var IsConstructorMethod = method.IsConstructor;
|
if (IsConstructorMethod)
|
{
|
//methodParams = new object[MethodVariables.Count - 1];
|
for (int i = 1; i < MethodVariables.Count; i++)
|
{
|
MethodVariable mv = MethodVariables[i];
|
if (mv.Direction == VariableDirection.In)
|
{
|
mv.Value = PlanRun.Resolve(this, mv.ValueStr, mv.Type); // mv.GetValue(PlanRun);
|
}
|
//methodParams[i-1] = mv.GetValue(PlanRun);
|
}
|
}
|
else if (IsStatic)
|
{
|
//methodParams = new object[MethodVariables.Count - 2];
|
for (int i = 1; i < MethodVariables.Count; i++)
|
{
|
MethodVariable mv = MethodVariables[i];
|
if (mv.Direction == VariableDirection.In)
|
{
|
mv.Value = PlanRun.Resolve(this, mv.ValueStr, mv.Type); //mv.GetValue(PlanRun);
|
}
|
//methodParams[i] = mv.GetValue(PlanRun);
|
}
|
}
|
else
|
{
|
//methodParams = new object[MethodVariables.Count - 1];
|
for (int i = 1; i < MethodVariables.Count; i++)
|
{
|
var mv = MethodVariables[i];
|
if (mv.Direction == VariableDirection.In)
|
{
|
mv.Value = PlanRun.Resolve(this, mv.ValueStr, mv.Type); //mv.GetValue(PlanRun);
|
}
|
//methodParams[i] = mv.GetValue(PlanRun);
|
}
|
}
|
}
|
|
/// <summary>
|
/// ÔËÐк¯Êý
|
/// </summary>
|
private void RunMethod()
|
{
|
ResolveVariables();
|
|
var method = methodCache[methodName];
|
var IsStatic = method.IsStatic;
|
var IsConstructorMethod = method.IsConstructor;
|
|
if (IsConstructorMethod)
|
{
|
var constructor = (ConstructorInfo)methodCache[methodName];
|
var methodParams = new object[MethodVariables.Count - 1];
|
for (int i = 1; i < MethodVariables.Count; i++)
|
{
|
var mv = MethodVariables[i];
|
methodParams[i - 1] = mv.Value;
|
}
|
instance = constructor.Invoke(methodParams);
|
MethodVariables[0].Value = instance;
|
}
|
else if (IsStatic)
|
{
|
var methodParams = new object[MethodVariables.Count - 1];
|
for (int i = 1; i < MethodVariables.Count; i++)
|
{
|
var mv = MethodVariables[i];
|
methodParams[i - 1] = mv.Value;
|
}
|
|
MethodVariables[0].Value = method.Invoke(null, methodParams);
|
for (int i = 0; i < methodParams.Length; i++)
|
{
|
MethodVariables[i + 1].Value = methodParams[i];
|
}
|
}
|
else
|
{
|
var methodParams = new object[MethodVariables.Count - 2];
|
for (int i = 2; i < MethodVariables.Count; i++)
|
{
|
var mv = MethodVariables[i];
|
methodParams[i - 2] = mv.Value;
|
}
|
instance = PlanRun.Resolve(this, MethodVariables[0].ValueStr);
|
MethodVariables[1].Value = method.Invoke(instance, methodParams);
|
for (int i = 0; i < methodParams.Length; i++)
|
{
|
MethodVariables[i + 2].Value = methodParams[i];
|
}
|
}
|
}
|
|
protected override void RunDetail()
|
{
|
try
|
{
|
// ToDo: Add test case code here
|
//RunChildSteps(); //If step has child steps.
|
RunMethod();
|
|
UpdateVariableToRuntime();
|
this.Verdict = Verdict.Pass;
|
}
|
catch (Exception ex)
|
{
|
Log.Error("DotNetStep Run error. {0}", ex);
|
this.Verdict = Verdict.Error;
|
}
|
}
|
}
|
}
|