This commit is contained in:
LocalizationBuildProcess 2019-08-21 16:24:11 -07:00
Родитель c5692beadc
Коммит 10e1e53eb6
32 изменённых файлов: 819 добавлений и 755 удалений

Просмотреть файл

@ -1,5 +1,8 @@
namespace Microsoft.Bot.Builder.AI.TriggerTrees
{
/// <summary>
/// Extension method to swap between <see cref="RelationshipType"/> "Generalizes" and "Specializes".
/// </summary>
public static partial class Extensions
{
public static RelationshipType Swap(this RelationshipType original)

Просмотреть файл

@ -7,6 +7,9 @@ using Microsoft.Bot.Builder.Dialogs.Debugging;
namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Events
{
/// <summary>
/// Extension method for <see cref="DialogContext"/> provides <see cref="DebugSupport"/>.
/// </summary>
public static partial class Extensions
{
public static async Task DebuggerStepAsync(this DialogContext context, IOnEvent rule, DialogEvent dialogEvent, CancellationToken cancellationToken)

Просмотреть файл

@ -28,69 +28,63 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
private readonly ConcurrentDictionary<string, ThreadModel> threadByTurnId = new ConcurrentDictionary<string, ThreadModel>();
private readonly Identifier<ThreadModel> threads = new Identifier<ThreadModel>();
private readonly Task task;
private int sequence = 0;
public DebugAdapter(int port, Source.IRegistry registry, IBreakpoints breakpoints, Action terminate, IEvents events = null, ICodeModel codeModel = null, IDataModel dataModel = null, ILogger logger = null, ICoercion coercion = null)
: base(logger)
{
this.events = events ?? new Events<DialogEvents>();
this.codeModel = codeModel ?? new CodeModel();
this.dataModel = dataModel ?? new DataModel(coercion ?? new Coercion());
this.registry = registry ?? throw new ArgumentNullException(nameof(registry));
this.breakpoints = breakpoints ?? throw new ArgumentNullException(nameof(breakpoints));
this.terminate = terminate ?? new Action(() => Environment.Exit(0));
this.task = ListenAsync(new IPEndPoint(IPAddress.Any, port), cancellationToken.Token);
}
/// <summary>
/// Thread debugging phases.
/// </summary>
public enum Phase
{
/// <summary>
/// "Started" signals Visual Studio Code that there is a new thread.
/// </summary>
Started,
/// <summary>
/// Follows "Next".
/// </summary>
Continue,
/// <summary>
/// Signal to "Step" or to "Ccontinue".
/// </summary>
Next,
/// <summary>
/// Follows "Next".
/// </summary>
Step,
/// <summary>
/// At breakpoint?
/// </summary>
Breakpoint,
/// <summary>
/// Thread paused.
/// </summary>
Pause,
/// <summary>
/// Thread exited.
/// </summary>
Exited
}
private sealed class ThreadModel
{
public ThreadModel(ITurnContext turnContext, ICodeModel codeModel)
{
TurnContext = turnContext;
CodeModel = codeModel;
}
public ITurnContext TurnContext { get; }
public ICodeModel CodeModel { get; }
public string Name => TurnContext.Activity.Text;
public IReadOnlyList<ICodePoint> Frames => CodeModel.PointsFor(LastContext, LastItem, LastMore);
public RunModel Run { get; } = new RunModel();
public Identifier<ICodePoint> FrameCodes { get; } = new Identifier<ICodePoint>();
public Identifier<object> ValueCodes { get; } = new Identifier<object>();
public DialogContext LastContext { get; set; }
public object LastItem { get; set; }
public string LastMore { get; set; }
}
public sealed class RunModel
{
public Phase? PhaseSent { get; set; }
public Phase Phase { get; set; } = Phase.Started;
public object Gate { get; } = new object();
public void Post(Phase what)
{
Monitor.Enter(Gate);
try
{
Phase = what;
Monitor.Pulse(Gate);
}
finally
{
Monitor.Exit(Gate);
}
}
}
private ulong EncodeValue(ThreadModel thread, object value)
{
if (dataModel.IsScalar(value))
@ -124,18 +118,96 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
frame = thread.FrameCodes[valueCode];
}
private readonly Task task;
public DebugAdapter(int port, Source.IRegistry registry, IBreakpoints breakpoints, Action terminate, IEvents events = null, ICodeModel codeModel = null, IDataModel dataModel = null, ILogger logger = null, ICoercion coercion = null)
: base(logger)
public static string Ellipsis(string text, int length)
{
this.events = events ?? new Events<DialogEvents>();
this.codeModel = codeModel ?? new CodeModel();
this.dataModel = dataModel ?? new DataModel(coercion ?? new Coercion());
this.registry = registry ?? throw new ArgumentNullException(nameof(registry));
this.breakpoints = breakpoints ?? throw new ArgumentNullException(nameof(breakpoints));
this.terminate = terminate ?? new Action(() => Environment.Exit(0));
this.task = ListenAsync(new IPEndPoint(IPAddress.Any, port), cancellationToken.Token);
if (text == null)
{
return string.Empty;
}
if (text.Length <= length)
{
return text;
}
int pos = text.IndexOf(" ", length);
if (pos >= 0)
{
return text.Substring(0, pos) + "...";
}
return text;
}
async Task DebugSupport.IDebugger.StepAsync(DialogContext context, object item, string more, CancellationToken cancellationToken)
{
try
{
var turnText = context.Context.Activity.Text?.Trim() ?? string.Empty;
if (turnText.Length == 0)
{
turnText = context.Context.Activity.Type;
}
var threadText = $"'{Ellipsis(turnText, 18)}'";
await OutputAsync($"{threadText} ==> {more?.PadRight(16) ?? string.Empty} ==> {codeModel.NameFor(item)} ", item, cancellationToken).ConfigureAwait(false);
await UpdateBreakpointsAsync(cancellationToken).ConfigureAwait(false);
if (threadByTurnId.TryGetValue(TurnIdFor(context.Context), out ThreadModel thread))
{
thread.LastContext = context;
thread.LastItem = item;
thread.LastMore = more;
var run = thread.Run;
if (breakpoints.IsBreakPoint(item) && events[more])
{
run.Post(Phase.Breakpoint);
}
// TODO: implement asynchronous condition variables
Monitor.Enter(run.Gate);
try
{
// TODO: remove synchronous waits
UpdateThreadPhaseAsync(thread, item, cancellationToken).GetAwaiter().GetResult();
// while the stopped condition is true, atomically release the mutex
while (!(run.Phase == Phase.Started || run.Phase == Phase.Continue || run.Phase == Phase.Next))
{
Monitor.Wait(run.Gate);
}
// "Started" signals to Visual Studio Code that there is a new thread
if (run.Phase == Phase.Started)
{
run.Phase = Phase.Continue;
}
// TODO: remove synchronous waits
UpdateThreadPhaseAsync(thread, item, cancellationToken).GetAwaiter().GetResult();
// allow one step to progress since next was requested
if (run.Phase == Phase.Next)
{
run.Phase = Phase.Step;
}
}
finally
{
Monitor.Exit(run.Gate);
}
}
else
{
this.logger.LogError($"thread context not found");
}
}
catch (Exception error)
{
this.logger.LogError(error, error.Message);
}
}
public async Task DisposeAsync()
@ -172,103 +244,39 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
}
}
protected override async Task AcceptAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
var token = await ReadAsync(cancellationToken).ConfigureAwait(false);
var request = Protocol.Parse(token);
Protocol.Message message;
try
{
message = await DispatchAsync(request, cancellationToken).ConfigureAwait(false);
}
catch (Exception error)
{
message = Protocol.Response.Fail(NextSeq, request, error.Message);
}
await SendAsync(message, cancellationToken).ConfigureAwait(false);
}
catch (Exception error)
{
this.logger.LogError(error, error.Message);
throw;
}
}
}
private static string TurnIdFor(ITurnContext turnContext)
{
return $"{turnContext.Activity.ChannelId}-{turnContext.Activity.Id}";
}
static public string Ellipsis(string text, int length)
{
if (text == null)
{
return string.Empty;
}
if (text.Length <= length)
{
return text;
}
int pos = text.IndexOf(" ", length);
if (pos >= 0)
{
return text.Substring(0, pos) + "...";
}
return text;
}
async Task DebugSupport.IDebugger.StepAsync(DialogContext context, object item, string more, CancellationToken cancellationToken)
{
try
{
var turnText = context.Context.Activity.Text?.Trim() ?? string.Empty;
if (turnText.Length == 0)
{
turnText = context.Context.Activity.Type;
}
var threadText = $"'{Ellipsis(turnText, 18)}'";
await OutputAsync($"{threadText} ==> {more?.PadRight(16) ?? ""} ==> {codeModel.NameFor(item)} ", item, cancellationToken).ConfigureAwait(false);
await UpdateBreakpointsAsync(cancellationToken).ConfigureAwait(false);
if (threadByTurnId.TryGetValue(TurnIdFor(context.Context), out ThreadModel thread))
{
thread.LastContext = context;
thread.LastItem = item;
thread.LastMore = more;
var run = thread.Run;
if (breakpoints.IsBreakPoint(item) && events[more])
{
run.Post(Phase.Breakpoint);
}
// TODO: implement asynchronous condition variables
Monitor.Enter(run.Gate);
try
{
// TODO: remove synchronous waits
UpdateThreadPhaseAsync(thread, item, cancellationToken).GetAwaiter().GetResult();
// while the stopped condition is true, atomically release the mutex
while (!(run.Phase == Phase.Started || run.Phase == Phase.Continue || run.Phase == Phase.Next))
{
Monitor.Wait(run.Gate);
}
// started is just use the signal to Visual Studio Code that there is a new thread
if (run.Phase == Phase.Started)
{
run.Phase = Phase.Continue;
}
// TODO: remove synchronous waits
UpdateThreadPhaseAsync(thread, item, cancellationToken).GetAwaiter().GetResult();
// allow one step to progress since next was requested
if (run.Phase == Phase.Next)
{
run.Phase = Phase.Step;
}
}
finally
{
Monitor.Exit(run.Gate);
}
}
else
{
this.logger.LogError($"thread context not found");
}
}
catch (Exception error)
{
this.logger.LogError(error, error.Message);
}
}
private async Task UpdateBreakpointsAsync(CancellationToken cancellationToken)
{
var breakpoints = this.breakpoints.ApplyUpdates();
@ -360,8 +368,6 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
await SendAsync(Protocol.Event.From(NextSeq, "output", body), cancellationToken).ConfigureAwait(false);
}
private int sequence = 0;
private int NextSeq => Interlocked.Increment(ref sequence);
private Protocol.Capabilities MakeCapabilities()
@ -605,32 +611,56 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
}
}
protected override async Task AcceptAsync(CancellationToken cancellationToken)
public sealed class RunModel
{
while (!cancellationToken.IsCancellationRequested)
public Phase? PhaseSent { get; set; }
public Phase Phase { get; set; } = Phase.Started;
public object Gate { get; } = new object();
public void Post(Phase what)
{
Monitor.Enter(Gate);
try
{
var token = await ReadAsync(cancellationToken).ConfigureAwait(false);
var request = Protocol.Parse(token);
Protocol.Message message;
try
{
message = await DispatchAsync(request, cancellationToken).ConfigureAwait(false);
}
catch (Exception error)
{
message = Protocol.Response.Fail(NextSeq, request, error.Message);
}
await SendAsync(message, cancellationToken).ConfigureAwait(false);
Phase = what;
Monitor.Pulse(Gate);
}
catch (Exception error)
finally
{
this.logger.LogError(error, error.Message);
throw;
Monitor.Exit(Gate);
}
}
}
private sealed class ThreadModel
{
public ThreadModel(ITurnContext turnContext, ICodeModel codeModel)
{
TurnContext = turnContext;
CodeModel = codeModel;
}
public ITurnContext TurnContext { get; }
public ICodeModel CodeModel { get; }
public string Name => TurnContext.Activity.Text;
public IReadOnlyList<ICodePoint> Frames => CodeModel.PointsFor(LastContext, LastItem, LastMore);
public RunModel Run { get; } = new RunModel();
public Identifier<ICodePoint> FrameCodes { get; } = new Identifier<ICodePoint>();
public Identifier<object> ValueCodes { get; } = new Identifier<object>();
public DialogContext LastContext { get; set; }
public object LastItem { get; set; }
public string LastMore { get; set; }
}
}
}

Просмотреть файл

@ -6,6 +6,22 @@ using static Microsoft.Bot.Builder.Dialogs.DialogContext;
namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
public interface IEvents
{
Protocol.ExceptionBreakpointFilter[] Filters
{
get;
}
bool this[string filter]
{
get;
set;
}
void Reset(IEnumerable<string> filters);
}
public sealed class Events<TDialogEvents> : IEvents
where TDialogEvents : DialogEvents
{
@ -31,6 +47,9 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
this.stateByFilter[DialogContext.DialogEvents.EndDialog] = false;
}
Protocol.ExceptionBreakpointFilter[] IEvents.Filters =>
this.stateByFilter.Select(kv => new Protocol.ExceptionBreakpointFilter() { Label = kv.Key, Filter = kv.Key, Default = kv.Value }).ToArray();
bool IEvents.this[string filter]
{
get => this.stateByFilter.TryGetValue(filter, out var state) ? state : false;
@ -45,23 +64,5 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
stateByFilter[filter] = index.Contains(filter);
}
}
Protocol.ExceptionBreakpointFilter[] IEvents.Filters => this.stateByFilter.Select(kv => new Protocol.ExceptionBreakpointFilter() { Label = kv.Key, Filter = kv.Key, Default = kv.Value }).ToArray();
}
public interface IEvents
{
Protocol.ExceptionBreakpointFilter[] Filters
{
get;
}
void Reset(IEnumerable<string> filters);
bool this[string filter]
{
get;
set;
}
}
}

Просмотреть файл

@ -4,6 +4,9 @@ using System.Threading.Tasks;
namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
/// <summary>
/// Extension method implementing await for <see cref="SemaphoreSlim"/>.
/// </summary>
public static partial class Extensions
{
public static async Task<Releaser> WithWaitAsync(this SemaphoreSlim semaphore, CancellationToken cancellationToken)

Просмотреть файл

@ -64,14 +64,12 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
private readonly ICoercion coercion;
public abstract int Rank { get; }
protected DataModelBase(ICoercion coercion)
{
this.coercion = coercion ?? throw new ArgumentNullException(nameof(coercion));
}
protected T Coerce<T>(object item) => (T)this.coercion.Coerce(item, typeof(T));
public abstract int Rank { get; }
public abstract TValue this[TContext context, TName name]
{
@ -79,16 +77,16 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
set;
}
public virtual bool IsScalar(TContext context) => false;
public abstract IEnumerable<TName> Names(TContext context);
object IDataModel.this[object context, object name]
{
get => this[(TContext)context, Coerce<TName>(name)];
set => this[(TContext)context, Coerce<TName>(name)] = Coerce<TValue>(value);
}
public virtual bool IsScalar(TContext context) => false;
public abstract IEnumerable<TName> Names(TContext context);
public virtual string ToString(TContext context) => (context is ICollection collection)
? $"Count = {collection.Count}"
: context.ToString();
@ -97,6 +95,8 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
string IDataModel.ToString(object context) => ToString((TContext)context);
protected T Coerce<T>(object item) => (T)this.coercion.Coerce(item, typeof(T));
IEnumerable<object> IDataModel.Names(object context) => Names((TContext)context).Cast<object>();
}
@ -210,6 +210,12 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
set => ModelFor(context)[context, name] = value;
}
bool IDataModel.IsScalar(object context) => ModelFor(context).IsScalar(context);
IEnumerable<object> IDataModel.Names(object context) => ModelFor(context).Names(context);
string IDataModel.ToString(object context) => ModelFor(context).ToString(context);
private IDataModel Create(Type definition, params Type[] typeArguments) =>
(IDataModel)Activator.CreateInstance(definition.MakeGenericType(typeArguments), this.coercion);
@ -270,11 +276,5 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
return model;
}
bool IDataModel.IsScalar(object context) => ModelFor(context).IsScalar(context);
IEnumerable<object> IDataModel.Names(object context) => ModelFor(context).Names(context);
string IDataModel.ToString(object context) => ModelFor(context).ToString(context);
}
}

Просмотреть файл

@ -10,6 +10,21 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
private const ulong MORE = 0x80;
private const ulong DATA = 0x7F;
public static ulong Encode(ulong one, ulong two)
{
ulong target = 0;
int offset = 0;
Encode(one, ref target, ref offset);
Encode(two, ref target, ref offset);
return target;
}
public static void Decode(ulong item, out ulong one, out ulong two)
{
Decode(ref item, out one);
Decode(ref item, out two);
}
private static void Encode(ulong source, ref ulong target, ref int offset)
{
while (source > DATA)
@ -45,21 +60,6 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
offset += 7;
}
}
public static ulong Encode(ulong one, ulong two)
{
ulong target = 0;
int offset = 0;
Encode(one, ref target, ref offset);
Encode(two, ref target, ref offset);
return target;
}
public static void Decode(ulong item, out ulong one, out ulong two)
{
Decode(ref item, out one);
Decode(ref item, out two);
}
}
/// <summary>
@ -71,6 +71,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
/// Some identifiers have a lifetime scoped to a thread (e.g. values or stack frames)
/// For these combined identifiers, we use 7 bit encoding.
/// </summary>
/// <typeparam name="T">Datatype of the stored items.</typeparam>
public sealed class Identifier<T> : IEnumerable<KeyValuePair<ulong, T>>
{
private readonly Dictionary<T, ulong> codeByItem = new Dictionary<T, ulong>(ReferenceEquality<T>.Instance);

Просмотреть файл

@ -7,6 +7,41 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
// https://github.com/Microsoft/debug-adapter-protocol/blob/gh-pages/debugAdapterProtocol.json
public static class Protocol
{
public static Request Parse(JToken token)
{
switch ((string)token["type"])
{
case "request":
switch ((string)token["command"])
{
case "launch": return token.ToObject<Request<Launch>>();
case "attach": return token.ToObject<Request<Attach>>();
case "initialize": return token.ToObject<Request<Initialize>>();
case "setBreakpoints": return token.ToObject<Request<SetBreakpoints>>();
case "setFunctionBreakpoints": return token.ToObject<Request<SetFunctionBreakpoints>>();
case "setExceptionBreakpoints": return token.ToObject<Request<SetExceptionBreakpoints>>();
case "configurationDone": return token.ToObject<Request<ConfigurationDone>>();
case "threads": return token.ToObject<Request<Threads>>();
case "stackTrace": return token.ToObject<Request<StackTrace>>();
case "scopes": return token.ToObject<Request<Scopes>>();
case "variables": return token.ToObject<Request<Variables>>();
case "setVariable": return token.ToObject<Request<SetVariable>>();
case "evaluate": return token.ToObject<Request<Evaluate>>();
case "continue": return token.ToObject<Request<Continue>>();
case "pause": return token.ToObject<Request<Pause>>();
case "next": return token.ToObject<Request<Next>>();
case "stepIn": return token.ToObject<Request<Next>>();
case "stepOut": return token.ToObject<Request<Next>>();
case "terminate": return token.ToObject<Request<Terminate>>();
case "disconnect": return token.ToObject<Request<Disconnect>>();
default: return token.ToObject<Request>();
}
default:
throw new NotImplementedException();
}
}
public abstract class Message
{
public int Seq { get; set; }
@ -290,40 +325,5 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
public string Name { get; set; }
}
public static Request Parse(JToken token)
{
switch ((string)token["type"])
{
case "request":
switch ((string)token["command"])
{
case "launch": return token.ToObject<Request<Launch>>();
case "attach": return token.ToObject<Request<Attach>>();
case "initialize": return token.ToObject<Request<Initialize>>();
case "setBreakpoints": return token.ToObject<Request<SetBreakpoints>>();
case "setFunctionBreakpoints": return token.ToObject<Request<SetFunctionBreakpoints>>();
case "setExceptionBreakpoints": return token.ToObject<Request<SetExceptionBreakpoints>>();
case "configurationDone": return token.ToObject<Request<ConfigurationDone>>();
case "threads": return token.ToObject<Request<Threads>>();
case "stackTrace": return token.ToObject<Request<StackTrace>>();
case "scopes": return token.ToObject<Request<Scopes>>();
case "variables": return token.ToObject<Request<Variables>>();
case "setVariable": return token.ToObject<Request<SetVariable>>();
case "evaluate": return token.ToObject<Request<Evaluate>>();
case "continue": return token.ToObject<Request<Continue>>();
case "pause": return token.ToObject<Request<Pause>>();
case "next": return token.ToObject<Request<Next>>();
case "stepIn": return token.ToObject<Request<Next>>();
case "stepOut": return token.ToObject<Request<Next>>();
case "terminate": return token.ToObject<Request<Terminate>>();
case "disconnect": return token.ToObject<Request<Disconnect>>();
default: return token.ToObject<Request>();
}
default:
throw new NotImplementedException();
}
}
}
}

Просмотреть файл

@ -12,11 +12,42 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
private readonly Dictionary<object, Source.Range> sourceByItem = new Dictionary<object, Source.Range>(ReferenceEquality<object>.Instance);
private bool dirty = true;
private readonly Identifier<Row> rows = new Identifier<Row>();
private readonly HashSet<object> items = new HashSet<object>(ReferenceEquality<object>.Instance);
public SourceMap(ICodeModel codeModel)
{
this.codeModel = codeModel ?? throw new ArgumentNullException(nameof(codeModel));
}
public static bool Equals(Protocol.Range target, Source.Range source) =>
(target.Source == null && source == null)
|| (PathEquals(target.Source.Path, source.Path)
&& target.Line == source.Start.LineIndex
&& target.EndLine == source.After.LineIndex
&& target.Column == source.Start.CharIndex
&& target.EndColumn == source.After.CharIndex);
public static void Assign(Protocol.Range target, Source.Range source)
{
if (source != null)
{
target.Source = new Protocol.Source(source.Path);
target.Line = source.Start.LineIndex;
target.EndLine = source.After.LineIndex;
target.Column = source.Start.CharIndex;
target.EndColumn = source.After.CharIndex;
}
else
{
target.Source = null;
target.Line = null;
target.EndLine = null;
target.Column = null;
target.EndColumn = null;
}
}
void Source.IRegistry.Add(object item, Source.Range range)
{
if (!Path.IsPathRooted(range.Path))
@ -47,58 +78,89 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
}
}
public sealed class Row
IReadOnlyList<Protocol.Breakpoint> IBreakpoints.SetBreakpoints(Protocol.Source source, IReadOnlyList<Protocol.SourceBreakpoint> sourceBreakpoints)
{
public Row(Protocol.Source source, Protocol.SourceBreakpoint sourceBreakpoint)
lock (gate)
{
Source = source;
SourceBreakpoint = sourceBreakpoint;
var path = source.Path;
foreach (var row in rows.Items)
{
if (row.FunctionBreakpoint == null && PathEquals(row.Source.Path, path))
{
rows.Remove(row);
}
}
var breakpoints = new List<Protocol.Breakpoint>(sourceBreakpoints.Count);
foreach (var sourceBreakpoint in sourceBreakpoints)
{
var row = new Row(source, sourceBreakpoint);
TryUpdate(row);
breakpoints.Add(row.Breakpoint);
}
RebuildItems();
return breakpoints;
}
public Row(Protocol.FunctionBreakpoint functionBreakpoint)
{
FunctionBreakpoint = functionBreakpoint;
}
public Protocol.Source Source { get; }
public Protocol.SourceBreakpoint SourceBreakpoint { get; }
public Protocol.FunctionBreakpoint FunctionBreakpoint { get; }
public Protocol.Breakpoint Breakpoint { get; } = new Protocol.Breakpoint();
public object Item { get; set; }
}
private readonly Identifier<Row> rows = new Identifier<Row>();
private readonly HashSet<object> items = new HashSet<object>(ReferenceEquality<object>.Instance);
public static bool Equals(Protocol.Range target, Source.Range source) =>
(target.Source == null && source == null)
|| (PathEquals(target.Source.Path, source.Path)
&& target.Line == source.Start.LineIndex
&& target.EndLine == source.After.LineIndex
&& target.Column == source.Start.CharIndex
&& target.EndColumn == source.After.CharIndex);
public static void Assign(Protocol.Range target, Source.Range source)
IReadOnlyList<Protocol.Breakpoint> IBreakpoints.SetBreakpoints(IReadOnlyList<Protocol.FunctionBreakpoint> functionBreakpoints)
{
if (source != null)
lock (gate)
{
target.Source = new Protocol.Source(source.Path);
target.Line = source.Start.LineIndex;
target.EndLine = source.After.LineIndex;
target.Column = source.Start.CharIndex;
target.EndColumn = source.After.CharIndex;
foreach (var row in rows.Items)
{
if (row.FunctionBreakpoint != null)
{
rows.Remove(row);
}
}
var breakpoints = new List<Protocol.Breakpoint>(functionBreakpoints.Count);
foreach (var functionBreakpoint in functionBreakpoints)
{
var row = new Row(functionBreakpoint);
TryUpdate(row);
breakpoints.Add(row.Breakpoint);
}
RebuildItems();
return breakpoints;
}
else
}
IReadOnlyList<Protocol.Breakpoint> IBreakpoints.ApplyUpdates()
{
lock (gate)
{
target.Source = null;
target.Line = null;
target.EndLine = null;
target.Column = null;
target.EndColumn = null;
IReadOnlyList<Protocol.Breakpoint> updates = Array.Empty<Protocol.Breakpoint>();
if (dirty)
{
updates = Update();
dirty = false;
}
return updates;
}
}
bool IBreakpoints.IsBreakPoint(object item)
{
lock (gate)
{
return this.items.Contains(item);
}
}
object IBreakpoints.ItemFor(Protocol.Breakpoint breakpoint)
{
lock (gate)
{
return this.rows[breakpoint.Id].Item;
}
}
@ -198,90 +260,28 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
}
}
IReadOnlyList<Protocol.Breakpoint> IBreakpoints.SetBreakpoints(Protocol.Source source, IReadOnlyList<Protocol.SourceBreakpoint> sourceBreakpoints)
public sealed class Row
{
lock (gate)
public Row(Protocol.Source source, Protocol.SourceBreakpoint sourceBreakpoint)
{
var path = source.Path;
foreach (var row in rows.Items)
{
if (row.FunctionBreakpoint == null && PathEquals(row.Source.Path, path))
{
rows.Remove(row);
}
}
var breakpoints = new List<Protocol.Breakpoint>(sourceBreakpoints.Count);
foreach (var sourceBreakpoint in sourceBreakpoints)
{
var row = new Row(source, sourceBreakpoint);
TryUpdate(row);
breakpoints.Add(row.Breakpoint);
}
RebuildItems();
return breakpoints;
Source = source;
SourceBreakpoint = sourceBreakpoint;
}
}
IReadOnlyList<Protocol.Breakpoint> IBreakpoints.SetBreakpoints(IReadOnlyList<Protocol.FunctionBreakpoint> functionBreakpoints)
{
lock (gate)
public Row(Protocol.FunctionBreakpoint functionBreakpoint)
{
foreach (var row in rows.Items)
{
if (row.FunctionBreakpoint != null)
{
rows.Remove(row);
}
}
var breakpoints = new List<Protocol.Breakpoint>(functionBreakpoints.Count);
foreach (var functionBreakpoint in functionBreakpoints)
{
var row = new Row(functionBreakpoint);
TryUpdate(row);
breakpoints.Add(row.Breakpoint);
}
RebuildItems();
return breakpoints;
FunctionBreakpoint = functionBreakpoint;
}
}
IReadOnlyList<Protocol.Breakpoint> IBreakpoints.ApplyUpdates()
{
lock (gate)
{
IReadOnlyList<Protocol.Breakpoint> updates = Array.Empty<Protocol.Breakpoint>();
if (dirty)
{
updates = Update();
dirty = false;
}
public Protocol.Source Source { get; }
return updates;
}
}
public Protocol.SourceBreakpoint SourceBreakpoint { get; }
bool IBreakpoints.IsBreakPoint(object item)
{
lock (gate)
{
return this.items.Contains(item);
}
}
public Protocol.FunctionBreakpoint FunctionBreakpoint { get; }
object IBreakpoints.ItemFor(Protocol.Breakpoint breakpoint)
{
lock (gate)
{
return this.rows[breakpoint.Id].Item;
}
public Protocol.Breakpoint Breakpoint { get; } = new Protocol.Breakpoint();
public object Item { get; set; }
}
}

Просмотреть файл

@ -37,6 +37,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Declarative.Plugins
this.Loader = Activator.CreateInstance(assembly.GetType(info.CustomLoaderClassName)) as ICustomDeserializer;
}
await Task.FromResult<object>(null);
return;
}
}

Просмотреть файл

@ -4,7 +4,10 @@ using static Microsoft.Bot.Builder.Dialogs.Debugging.Source;
namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
public static partial class DebugSupport
/// <summary>
/// Debugger support for <see cref="ITurnContext"/>, <see cref="DialogContext"/>.
/// </summary>
public static class DebugSupport
{
public interface IDebugger
{

Просмотреть файл

@ -3,7 +3,7 @@
namespace Microsoft.Bot.Builder.Dialogs
{
/// <summary>
/// Defines interface for a LG system to bind to text.
/// Defines interface for a Language Generator system to bind to text.
/// </summary>
public interface ILanguageGenerator
{

Просмотреть файл

@ -155,7 +155,13 @@ namespace Microsoft.Bot.Builder.Dialogs
return activity;
}
protected async Task<object> ReadAttachmentFile(ITurnContext turnContext, ILanguageGenerator languageGenerator, string fileLocation, string contentType, bool isCard, object data)
protected async Task<object> ReadAttachmentFile(
ITurnContext turnContext,
ILanguageGenerator languageGenerator,
string fileLocation,
string contentType,
bool isCard,
object data)
{
if (Uri.TryCreate(fileLocation, UriKind.Absolute, out Uri uri))
{

Просмотреть файл

@ -1,7 +1,13 @@
grammar CommonRegex;
@parser::header {#pragma warning disable 3021} // Disable StyleCop warning CS3021 re CLSCompliant attribute in generated files.
@lexer::header {#pragma warning disable 3021} // Disable StyleCop warning CS3021 re CLSCompliant attribute in generated files.
@parser::header {
#pragma warning disable 3021 // Disable StyleCop warning CS3021 re CLSCompliant attribute in generated files.
#pragma warning disable 0108 // Disable StyleCop warning CS0108, hides inherited member in generated files.
}
@lexer::header {
#pragma warning disable 3021 // Disable StyleCop warning CS3021 re CLSCompliant attribute in generated files.
#pragma warning disable 0108 // Disable StyleCop warning CS0108, hides inherited member in generated files.
}
parse
: alternation EOF

Просмотреть файл

@ -7,6 +7,9 @@ using System.Linq;
namespace Microsoft.Bot.Builder.Expressions
{
/// <summary>
/// Extension methods for detecting or value testing of various types.
/// </summary>
public static partial class Extensions
{
/// <summary>

Просмотреть файл

@ -6,6 +6,9 @@ using System.Linq;
namespace Microsoft.Bot.Builder.Expressions
{
/// <summary>
/// Extension methods for manipulation of <see cref="Expression"/> values.
/// </summary>
public static partial class Extensions
{
/// <summary>

Просмотреть файл

@ -21,7 +21,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
public ILanguagePolicy LanguagePolicy { get; set; } = new LanguagePolicy();
/// <summary>
/// abstract method to lookup a ILanguageGenerator by locale.
/// Abstract method to get an ILanguageGenerator by locale.
/// </summary>
/// <param name="context">context.</param>
/// <param name="locale">locale.</param>
@ -30,12 +30,12 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
public abstract bool TryGetGenerator(ITurnContext context, string locale, out ILanguageGenerator generator);
/// <summary>
/// Find language generators that match the current context locale??
/// Find a language generator that matches the current context locale.
/// </summary>
/// <param name="turnContext">Context for the current turn of conversation.</param>
/// <param name="template">The template.</param>
/// <param name="data"></param>
/// <returns></returns>
/// <param name="data">data to bind to.</param>
/// <returns>The generator.</returns>
public async Task<string> Generate(ITurnContext turnContext, string template, object data)
{
// see if we have any locales that match

Просмотреть файл

@ -5,6 +5,11 @@ using Microsoft.Bot.Builder.Expressions;
namespace Microsoft.Bot.Builder.LanguageGeneration
{
public interface IGetMethod
{
ExpressionEvaluator GetMethodX(string name);
}
internal class GetMethodExtensions : IGetMethod
{
// Hold an evaluator instance to make sure all functions have access
@ -61,9 +66,4 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
}
}
}
public interface IGetMethod
{
ExpressionEvaluator GetMethodX(string name);
}
}

Просмотреть файл

@ -4,7 +4,7 @@
namespace Microsoft.Bot.Schema
{
/// <summary>
/// Extension methods for converting strongly typed Card objects to Attachement.
/// Extension methods for converting strongly typed Card objects to <see cref="Attachment"/>.
/// </summary>
public static partial class Extensions
{

Просмотреть файл

@ -6,54 +6,6 @@ using Microsoft.Bot.Builder.Expressions;
namespace Microsoft.Bot.Builder.AI.TriggerTrees.Tests
{
public class Comparison
{
public string Type;
public object Value;
public Comparison(string type, object value)
{
Type = type;
Value = value;
}
}
public class ExpressionInfo
{
public Expression Expression;
public Dictionary<string, Comparison> Bindings = new Dictionary<string, Comparison>();
public List<Quantifier> Quantifiers = new List<Quantifier>();
public ExpressionInfo(Expression expression)
{
Expression = expression;
}
public ExpressionInfo(Expression expression, string name, object value, string type)
{
Expression = expression;
Bindings.Add(name, new Comparison(type, value));
}
public ExpressionInfo(Expression expression, Dictionary<string, Comparison> bindings, List<Quantifier> quantifiers = null)
{
Expression = expression;
Bindings = bindings;
if (quantifiers != null)
{
Quantifiers = quantifiers;
}
}
public override string ToString() => Expression.ToString();
}
public class TriggerInfo
{
public Expression Trigger;
public Dictionary<string, object> Bindings = new Dictionary<string, object>();
}
public class Generator
{
public Random Rand;
@ -91,33 +43,6 @@ namespace Microsoft.Bot.Builder.AI.TriggerTrees.Tests
return builder.ToString();
}
private int AdjustValue(int value, string type)
{
var result = value;
const int epsilon = 1;
switch (type)
{
case ExpressionType.LessThan: result += epsilon; break;
case ExpressionType.NotEqual: result += epsilon; break;
case ExpressionType.GreaterThan: result -= epsilon; break;
}
return result;
}
private double AdjustValue(double value, string type)
{
var result = value;
switch (type)
{
case ExpressionType.LessThan: result += DoubleEpsilon; break;
case ExpressionType.NotEqual: result += DoubleEpsilon; break;
case ExpressionType.GreaterThan: result -= DoubleEpsilon; break;
}
return result;
}
public ExpressionInfo GenerateSimpleComparison(string name)
{
Expression expression = null;
@ -290,8 +215,8 @@ namespace Microsoft.Bot.Builder.AI.TriggerTrees.Tests
public Expression Binary(
string type,
IEnumerable<ExpressionInfo> expressions,
out Dictionary<string, Comparison> bindings)
IEnumerable<ExpressionInfo> expressions,
out Dictionary<string, Comparison> bindings)
{
bindings = MergeBindings(expressions);
Expression binaryExpression = null;
@ -318,21 +243,6 @@ namespace Microsoft.Bot.Builder.AI.TriggerTrees.Tests
}
}
private int SplitMemory(string mem, out string baseName)
{
var i = 0;
for (; i < mem.Length; ++i)
{
if (char.IsDigit(mem[i]))
{
break;
}
}
baseName = mem.Substring(0, i);
return int.Parse(mem.Substring(i));
}
public List<ExpressionInfo> GenerateQuantfiers(List<ExpressionInfo> predicates, int numExpressions, int maxVariable, int maxExpansion, int maxQuantifiers)
{
var result = new List<ExpressionInfo>();
@ -406,6 +316,137 @@ namespace Microsoft.Bot.Builder.AI.TriggerTrees.Tests
return result;
}
public IEnumerable<ExpressionInfo> GenerateNots(IList<ExpressionInfo> predicates, int numNots)
{
for (var i = 0; i < numNots; ++i)
{
var expr = RandomChoice(predicates);
var bindings = new Dictionary<string, Comparison>();
foreach (var binding in expr.Bindings)
{
var comparison = NotValue(binding.Value);
if (comparison != null)
{
bindings.Add(binding.Key, comparison);
}
}
yield return new ExpressionInfo(Expression.NotExpression(expr.Expression), bindings, expr.Quantifiers);
}
}
public Dictionary<string, Comparison> MergeBindings(IEnumerable<ExpressionInfo> expressions)
{
var bindings = new Dictionary<string, Comparison>();
foreach (var info in expressions)
{
foreach (var binding in info.Bindings)
{
bindings[binding.Key] = binding.Value;
}
}
return bindings;
}
public T RandomChoice<T>(IList<T> choices) => choices[Rand.Next(choices.Count)];
public T RandomWeighted<T>(IEnumerable<WeightedChoice<T>> choices)
{
var totalWeight = 0.0;
foreach (var choice in choices)
{
totalWeight += choice.Weight;
}
var selection = Rand.NextDouble() * totalWeight;
var soFar = 0.0;
var result = default(T);
foreach (var choice in choices)
{
if (soFar <= selection)
{
soFar += choice.Weight;
result = choice.Choice;
}
else
{
break;
}
}
return result;
}
public int RandomWeighted(IReadOnlyList<double> weights)
{
var totalWeight = 0.0;
foreach (var weight in weights)
{
totalWeight += weight;
}
var selection = Rand.NextDouble() * totalWeight;
var soFar = 0.0;
var result = 0;
for (var i = 0; i < weights.Count; ++i)
{
if (soFar <= selection)
{
soFar += weights[i];
result = i;
}
else
{
break;
}
}
return result;
}
private int SplitMemory(string mem, out string baseName)
{
var i = 0;
for (; i < mem.Length; ++i)
{
if (char.IsDigit(mem[i]))
{
break;
}
}
baseName = mem.Substring(0, i);
return int.Parse(mem.Substring(i));
}
private int AdjustValue(int value, string type)
{
var result = value;
const int epsilon = 1;
switch (type)
{
case ExpressionType.LessThan: result += epsilon; break;
case ExpressionType.NotEqual: result += epsilon; break;
case ExpressionType.GreaterThan: result -= epsilon; break;
}
return result;
}
private double AdjustValue(double value, string type)
{
var result = value;
switch (type)
{
case ExpressionType.LessThan: result += DoubleEpsilon; break;
case ExpressionType.NotEqual: result += DoubleEpsilon; break;
case ExpressionType.GreaterThan: result -= DoubleEpsilon; break;
}
return result;
}
private Comparison NotValue(Comparison comparison)
{
var value = comparison.Value;
@ -484,25 +525,6 @@ namespace Microsoft.Bot.Builder.AI.TriggerTrees.Tests
return isNot ? null : new Comparison(comparison.Type, value);
}
public IEnumerable<ExpressionInfo> GenerateNots(IList<ExpressionInfo> predicates, int numNots)
{
for (var i = 0; i < numNots; ++i)
{
var expr = RandomChoice(predicates);
var bindings = new Dictionary<string, Comparison>();
foreach (var binding in expr.Bindings)
{
var comparison = NotValue(binding.Value);
if (comparison != null)
{
bindings.Add(binding.Key, comparison);
}
}
yield return new ExpressionInfo(Expression.NotExpression(expr.Expression), bindings, expr.Quantifiers);
}
}
private Dictionary<Type, List<string>> VariablesByType(Dictionary<string, Comparison> bindings)
{
var result = new Dictionary<Type, List<string>>();
@ -519,83 +541,12 @@ namespace Microsoft.Bot.Builder.AI.TriggerTrees.Tests
return result;
}
public Dictionary<string, Comparison> MergeBindings(IEnumerable<ExpressionInfo> expressions)
{
var bindings = new Dictionary<string, Comparison>();
foreach (var info in expressions)
{
foreach (var binding in info.Bindings)
{
bindings[binding.Key] = binding.Value;
}
}
return bindings;
}
public T RandomChoice<T>(IList<T> choices) => choices[Rand.Next(choices.Count)];
public class WeightedChoice<T>
{
public double Weight = 0.0;
public T Choice = default(T);
}
public T RandomWeighted<T>(IEnumerable<WeightedChoice<T>> choices)
{
var totalWeight = 0.0;
foreach (var choice in choices)
{
totalWeight += choice.Weight;
}
var selection = Rand.NextDouble() * totalWeight;
var soFar = 0.0;
var result = default(T);
foreach (var choice in choices)
{
if (soFar <= selection)
{
soFar += choice.Weight;
result = choice.Choice;
}
else
{
break;
}
}
return result;
}
public int RandomWeighted(IReadOnlyList<double> weights)
{
var totalWeight = 0.0;
foreach (var weight in weights)
{
totalWeight += weight;
}
var selection = Rand.NextDouble() * totalWeight;
var soFar = 0.0;
var result = 0;
for (var i = 0; i < weights.Count; ++i)
{
if (soFar <= selection)
{
soFar += weights[i];
result = i;
}
else
{
break;
}
}
return result;
}
public class SimpleValues
{
public int Int = 1;
@ -621,6 +572,14 @@ namespace Microsoft.Bot.Builder.AI.TriggerTrees.Tests
{
Object = obj;
}
public static bool Test(SimpleValues obj, int? value) => value.HasValue && obj.Int == value;
public static bool Test(SimpleValues obj, double? value) => value.HasValue && obj.Double == value;
public static bool Test(SimpleValues obj, string value) => value != null && obj.String == value;
public static bool Test(SimpleValues obj, object other) => other != null && obj.Object.Equals(other);
public bool Test(int? value) => value.HasValue && Int == value;
@ -629,14 +588,54 @@ namespace Microsoft.Bot.Builder.AI.TriggerTrees.Tests
public bool Test(string value) => value != null && String == value;
public bool Test(SimpleValues value) => Int == value.Int && Double == value.Double && String == value.String && Object.Equals(value.Object);
public static bool Test(SimpleValues obj, int? value) => value.HasValue && obj.Int == value;
public static bool Test(SimpleValues obj, double? value) => value.HasValue && obj.Double == value;
public static bool Test(SimpleValues obj, string value) => value != null && obj.String == value;
public static bool Test(SimpleValues obj, object other) => other != null && obj.Object.Equals(other);
}
}
public class Comparison
{
public string Type;
public object Value;
public Comparison(string type, object value)
{
Type = type;
Value = value;
}
}
public class ExpressionInfo
{
public Expression Expression;
public Dictionary<string, Comparison> Bindings = new Dictionary<string, Comparison>();
public List<Quantifier> Quantifiers = new List<Quantifier>();
public ExpressionInfo(Expression expression)
{
Expression = expression;
}
public ExpressionInfo(Expression expression, string name, object value, string type)
{
Expression = expression;
Bindings.Add(name, new Comparison(type, value));
}
public ExpressionInfo(Expression expression, Dictionary<string, Comparison> bindings, List<Quantifier> quantifiers = null)
{
Expression = expression;
Bindings = bindings;
if (quantifiers != null)
{
Quantifiers = quantifiers;
}
}
public override string ToString() => Expression.ToString();
}
public class TriggerInfo
{
public Expression Trigger;
public Dictionary<string, object> Bindings = new Dictionary<string, object>();
}
}

Просмотреть файл

@ -553,6 +553,30 @@ namespace Microsoft.Bot.Builder.Azure.Tests
return _hasEmulator.Value;
}
private static async Task CreateCosmosDbWithPartitionedCollection(string partitionKey)
{
using (var client = new DocumentClient(new Uri(CosmosServiceEndpoint), CosmosAuthKey))
{
Database database = await client.CreateDatabaseIfNotExistsAsync(new Database { Id = CosmosDatabaseName });
var partitionKeyDefinition = new PartitionKeyDefinition { Paths = new Collection<string> { $"/{partitionKey}" } };
var collectionDefinition = new DocumentCollection { Id = CosmosCollectionName, PartitionKey = partitionKeyDefinition };
await client.CreateDocumentCollectionIfNotExistsAsync(database.SelfLink, collectionDefinition);
}
}
private static CosmosDbStorageOptions CreateCosmosDbStorageOptions(string partitionKey = "")
{
return new CosmosDbStorageOptions()
{
PartitionKey = partitionKey,
AuthKey = CosmosAuthKey,
CollectionId = CosmosCollectionName,
CosmosDBEndpoint = new Uri(CosmosServiceEndpoint),
DatabaseId = CosmosDatabaseName,
};
}
private Mock<IDocumentClient> GetDocumentClient()
{
var mock = new Mock<IDocumentClient>();
@ -578,30 +602,6 @@ namespace Microsoft.Bot.Builder.Azure.Tests
return mock;
}
private static async Task CreateCosmosDbWithPartitionedCollection(string partitionKey)
{
using (var client = new DocumentClient(new Uri(CosmosServiceEndpoint), CosmosAuthKey))
{
Database database = await client.CreateDatabaseIfNotExistsAsync(new Database { Id = CosmosDatabaseName });
var partitionKeyDefinition = new PartitionKeyDefinition { Paths = new Collection<string> { $"/{partitionKey}" } };
var collectionDefinition = new DocumentCollection { Id = CosmosCollectionName, PartitionKey = partitionKeyDefinition };
await client.CreateDocumentCollectionIfNotExistsAsync(database.SelfLink, collectionDefinition);
}
}
private static CosmosDbStorageOptions CreateCosmosDbStorageOptions(string partitionKey = "")
{
return new CosmosDbStorageOptions()
{
PartitionKey = partitionKey,
AuthKey = CosmosAuthKey,
CollectionId = CosmosCollectionName,
CosmosDBEndpoint = new Uri(CosmosServiceEndpoint),
DatabaseId = CosmosDatabaseName,
};
}
internal class StoreItem : IStoreItem
{
[JsonProperty(PropertyName = "messageList")]

Просмотреть файл

@ -164,7 +164,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Tests
Assert.AreEqual("joe", value);
Assert.AreEqual("default", state.GetValue("user.xxx", "default"));
Assert.AreEqual("default", state.GetValue<String>("user.xxx", "default"));
Assert.AreEqual("default", state.GetValue<string>("user.xxx", "default"));
Assert.IsFalse(state.TryGetValue<string>("user.xxx", out value));
Assert.AreEqual(null, value);
}

Просмотреть файл

@ -17,8 +17,6 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Tests
{
public IConfiguration Configuration;
public TestContext TestContext { get; set; }
public SettingsStateTests()
{
var builder = new ConfigurationBuilder()
@ -27,6 +25,8 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Tests
this.Configuration = builder.Build();
}
public TestContext TestContext { get; set; }
[TestMethod]
public async Task DialogContextState_SettingsTest()
{

Просмотреть файл

@ -36,11 +36,6 @@ namespace Microsoft.Bot.Builder.Dialogs.Composition.Recognizers.Tests
public TestContext TestContext { get; set; }
private TurnContext GetTurnContext(string text, string locale = "en-us")
{
return new TurnContext(new TestAdapter(TestAdapter.CreateConversation(TestContext.TestName)), new Schema.Activity(type: Schema.ActivityTypes.Message, text: text, locale: locale));
}
[TestMethod]
public void TestAge()
{
@ -211,5 +206,10 @@ namespace Microsoft.Bot.Builder.Dialogs.Composition.Recognizers.Tests
Assert.AreEqual(2, results.Count, "Should be 1 entities found");
Assert.AreEqual(1, results.Where(entity => entity.Type == "url").Count(), "Should have 1 url");
}
private TurnContext GetTurnContext(string text, string locale = "en-us")
{
return new TurnContext(new TestAdapter(TestAdapter.CreateConversation(TestContext.TestName)), new Schema.Activity(type: Schema.ActivityTypes.Message, text: text, locale: locale));
}
}
}

Просмотреть файл

@ -14,25 +14,6 @@ namespace Microsoft.Bot.Builder.Dialogs.Composition.Tests
public TestContext TestContext { get; set; }
public class RecognizerDialog : Dialog, IDialog
{
public IRecognizer Recognizer { get; set; }
public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken))
{
var result = await Recognizer.RecognizeAsync(dc.Context, cancellationToken);
await dc.Context.SendActivityAsync(dc.Context.Activity.CreateReply(result.Text));
return new DialogTurnResult(DialogTurnStatus.Waiting);
}
public override async Task<DialogTurnResult> ContinueDialogAsync(DialogContext dc, CancellationToken cancellationToken = default(CancellationToken))
{
var result = await Recognizer.RecognizeAsync(dc.Context, cancellationToken);
await dc.Context.SendActivityAsync(dc.Context.Activity.CreateReply(result.Text));
return new DialogTurnResult(DialogTurnStatus.Waiting);
}
}
private Activity CreateLocaleActivity(string locale)
{
var activity = Activity.CreateMessageActivity();
@ -40,6 +21,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Composition.Tests
activity.Locale = locale;
return (Activity)activity;
}
/// <summary>
/// Create test flow.
/// </summary>
@ -67,6 +49,25 @@ namespace Microsoft.Bot.Builder.Dialogs.Composition.Tests
return adapter;
}
public class RecognizerDialog : Dialog, IDialog
{
public IRecognizer Recognizer { get; set; }
public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken))
{
var result = await Recognizer.RecognizeAsync(dc.Context, cancellationToken);
await dc.Context.SendActivityAsync(dc.Context.Activity.CreateReply(result.Text));
return new DialogTurnResult(DialogTurnStatus.Waiting);
}
public override async Task<DialogTurnResult> ContinueDialogAsync(DialogContext dc, CancellationToken cancellationToken = default(CancellationToken))
{
var result = await Recognizer.RecognizeAsync(dc.Context, cancellationToken);
await dc.Context.SendActivityAsync(dc.Context.Activity.CreateReply(result.Text));
return new DialogTurnResult(DialogTurnStatus.Waiting);
}
}
}
public class TestRecognizer : IRecognizer
@ -80,13 +81,13 @@ namespace Microsoft.Bot.Builder.Dialogs.Composition.Tests
public async Task<RecognizerResult> RecognizeAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
return new RecognizerResult() { Text = this.Id };
return await Task.FromResult(new RecognizerResult() { Text = this.Id });
}
public async Task<T> RecognizeAsync<T>(ITurnContext turnContext, CancellationToken cancellationToken)
where T : IRecognizerConvert, new()
{
return new T();
return await Task.FromResult(new T());
}
}
}

Просмотреть файл

@ -12,7 +12,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Composition.Tests
{
public class TestUtilities
{
static Lazy<Dictionary<string, string>> environmentKeys = new Lazy<Dictionary<string, string>>(() =>
private static Lazy<Dictionary<string, string>> environmentKeys = new Lazy<Dictionary<string, string>>(() =>
{
try
{

Просмотреть файл

@ -10,22 +10,6 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
{
private TestContext testContextInstance;
/// <summary>
/// Gets or sets the test context which provides
/// information about and functionality for the current test run.
///</summary>
/// <value>
/// The test context which provides
/// information about and functionality for the current test run.
/// </value>
public TestContext TestContext
{
get { return testContextInstance; }
set { testContextInstance = value; }
}
public static object[] Test(string input) => new object[] { input };
public static IEnumerable<object[]> InvalidExpressions => new[]
{
Test("a+"),
@ -38,21 +22,7 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("user.lists.{dialog.listName}")
};
[DataTestMethod]
[DynamicData(nameof(InvalidExpressions))]
[ExpectedException(typeof(Exception))]
public void Parse(string exp)
{
try
{
new ExpressionEngine().Parse(exp);
}
catch (Exception e)
{
TestContext.WriteLine(e.Message);
throw e;
}
}
public static object[] Test(string input) => new object[] { input };
public static IEnumerable<object[]> BadExpressions => new[]
{
@ -292,12 +262,12 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
#endregion
#region uri parsing function test
Test("uriHost(relatibeUri)"),
Test("uriPath(relatibeUri)"),
Test("uriPathAndQuery(relatibeUri)"),
Test("uriPort(relatibeUri)"),
Test("uriQuery(relatibeUri)"),
Test("uriScheme(relatibeUri)"),
Test("uriHost(relativeUri)"),
Test("uriPath(relativeUri)"),
Test("uriPathAndQuery(relativeUri)"),
Test("uriPort(relativeUri)"),
Test("uriQuery(relativeUri)"),
Test("uriScheme(relativeUri)"),
#endregion
#region collection functions test
@ -399,6 +369,36 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
#endregion
};
/// <summary>
/// Gets or sets the test context which provides
/// information about and functionality for the current test run.
/// </summary>
/// <value>
/// The test context which provides
/// information about and functionality for the current test run.
/// </value>
public TestContext TestContext
{
get { return testContextInstance; }
set { testContextInstance = value; }
}
[DataTestMethod]
[DynamicData(nameof(InvalidExpressions))]
[ExpectedException(typeof(Exception))]
public void Parse(string exp)
{
try
{
new ExpressionEngine().Parse(exp);
}
catch (Exception e)
{
TestContext.WriteLine(e.Message);
throw e;
}
}
[DataTestMethod]
[DynamicData(nameof(BadExpressions))]
public void Evaluate(string exp)

Просмотреть файл

@ -10,10 +10,8 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
[TestClass]
public class ExpressionEngineTests
{
public static object[] Test(string input, object value, HashSet<string> paths = null) => new object[] { input, value, paths };
public static HashSet<string> one = new HashSet<string> { "one" };
public static HashSet<string> oneTwo = new HashSet<string> { "one", "two" };
public static HashSet<string> One = new HashSet<string> { "one" };
public static HashSet<string> OneTwo = new HashSet<string> { "one", "two" };
private static readonly string NullStr = null;
private readonly object scope = new Dictionary<string, object>
@ -97,7 +95,8 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
]
}
]
}" },
}"
},
{
"turn", new
{
@ -216,7 +215,7 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
};
public static IEnumerable<object[]> Data => new[]
{
{
#region SetPathToProperty test
// TODO: We should support this.
// Test("@@['c' + 'ity']", "Seattle"),
@ -262,7 +261,7 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("!!exists(one) == !!exists(one)", true),
Test("!(one == 1.0)", false, new HashSet<string> { "one" }),
Test("!!(one == 1.0)", true, new HashSet<string> { "one" }),
Test("!(one == 1.0) || !!(two == 2.0)", true, oneTwo),
Test("!(one == 1.0) || !!(two == 2.0)", true, OneTwo),
Test("!true", false),
Test("!!true", true),
Test("!(one == 1.0) || !!(two == 2.0)", true),
@ -282,7 +281,7 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("float(5.5) <= float(4 - 1)", false),
Test("'string'&'builder'", "stringbuilder"),
Test("\"string\"&\"builder\"", "stringbuilder"),
Test("one > 0.5 && two < 2.5", true, oneTwo),
Test("one > 0.5 && two < 2.5", true, OneTwo),
Test("notThere > 4", false),
Test("float(5.5) && float(0.0)", true),
Test("hello && \"hello\"", true),
@ -291,7 +290,7 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("!(hello)", false), // false
Test("!(10)", false),
Test("!(0)", false),
Test("one > 0.5 || two < 1.5", true, oneTwo),
Test("one > 0.5 || two < 1.5", true, OneTwo),
Test("one / 0 || two", true),
Test("0/3", 0),
Test("True == true", true),
@ -336,21 +335,21 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("addOrdinal(11 + 11)", "22nd"),
Test("addOrdinal(11 + 12)", "23rd"),
Test("addOrdinal(11 + 13)", "24th"),
Test("addOrdinal(-1)", "-1"), //original string value
Test("addOrdinal(-1)", "-1"), // original string value
# endregion
#endregion
# region Logical comparison functions test
#region Logical comparison functions test
Test("and(1 == 1, 1 < 2, 1 > 2)", false),
Test("and(!true, !!true)", false), //false && true
Test("and(!!true, !!true)", true), //true && true
Test("and(hello != 'world', bool('true'))", true), //true && true
Test("and(hello == 'world', bool('true'))", false), //false && true
Test("or(!exists(one), !!exists(one))", true), //false && true
Test("or(!exists(one), !exists(one))", false), //false && false
Test("greater(one, two)", false, oneTwo),
Test("and(!true, !!true)", false), // false && true
Test("and(!!true, !!true)", true), // true && true
Test("and(hello != 'world', bool('true'))", true), // true && true
Test("and(hello == 'world', bool('true'))", false), // false && true
Test("or(!exists(one), !!exists(one))", true), // false && true
Test("or(!exists(one), !exists(one))", false), // false && false
Test("greater(one, two)", false, OneTwo),
Test("greater(one , 0.5) && less(two , 2.5)", true), // true && true
Test("greater(one , 0.5) || less(two , 1.5)", true), //true || false
Test("greater(one , 0.5) || less(two , 1.5)", true), // true || false
Test("greater(5, 2)", true),
Test("greater(2, 2)", false),
Test("greater(one, two)", false),
@ -359,14 +358,14 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("greaterOrEquals(float(5.5) , float(4 - 1))", true),
Test("greaterOrEquals(one, one)", true),
Test("greaterOrEquals(one, two)", false),
Test("greaterOrEquals(one, one)", true, one),
Test("greaterOrEquals(one, two)", false, oneTwo),
Test("greaterOrEquals(one, one)", true, One),
Test("greaterOrEquals(one, two)", false, OneTwo),
Test("less(5, 2)", false),
Test("less(2, 2)", false),
Test("less(one, two)", true),
Test("less(one, two)", true, oneTwo),
Test("less(one, two)", true, OneTwo),
Test("lessOrEquals(one, one)", true, new HashSet<string> { "one" }),
Test("lessOrEquals(one, two)", true, oneTwo),
Test("lessOrEquals(one, two)", true, OneTwo),
Test("lessOrEquals(one, one)", true),
Test("lessOrEquals(one, two)", true),
Test("lessOrEquals((1 + 2) , (4 - 1))", true),
@ -377,13 +376,13 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("equals(hello, 'hello')", true),
Test("equals(bag.index, 3)", true),
Test("equals(bag.index, 2)", false),
Test("equals(hello == 'world', bool('true'))", false), //false, true
Test("equals(hello == 'world', bool(0))", false), //false, true
Test("if(!exists(one), 'r1', 'r2')", "r2"), //false
Test("if(!!exists(one), 'r1', 'r2')", "r1"), //true
Test("if(0, 'r1', 'r2')", "r1"), //true
Test("if(bool('true'), 'r1', 'r2')", "r1"), //true
Test("if(istrue, 'r1', 'r2')", "r1"), //true
Test("equals(hello == 'world', bool('true'))", false), // false, true
Test("equals(hello == 'world', bool(0))", false), // false, true
Test("if(!exists(one), 'r1', 'r2')", "r2"), // false
Test("if(!!exists(one), 'r1', 'r2')", "r1"), // true
Test("if(0, 'r1', 'r2')", "r1"), // true
Test("if(bool('true'), 'r1', 'r2')", "r1"), // true
Test("if(istrue, 'r1', 'r2')", "r1"), // true
Test("exists(one)", true),
Test("exists(xxx)", false),
Test("exists(one.xxx)", false),
@ -393,7 +392,7 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("not(one == 1.0)", false, new HashSet<string> { "one" }),
Test("not(not(one == 1.0))", true, new HashSet<string> { "one" }),
Test("not(false)", true),
Test("and(one > 0.5, two < 2.5)", true, oneTwo),
Test("and(one > 0.5, two < 2.5)", true, OneTwo),
Test("and(float(5.5), float(0.0))", true),
Test("and(hello, \"hello\")", true),
Test("or(items, (2 + 2) <= (4 - 1))", true), // true || false
@ -405,9 +404,9 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("if(null, 'r1', 'r2')", "r2"),
Test("if(hello * 5, 'r1', 'r2')", "r2"),
Test("if(10, 'r1', 'r2')", "r1"),
# endregion
#endregion
# region Conversion functions test
#region Conversion functions test
Test("float('10.333')", 10.333f),
Test("float('10')", 10.0f),
Test("int('10')", 10),
@ -448,7 +447,7 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("min(mul(1, 2), 5) ", 2),
Test("min(4, 5) ", 4),
Test("min(4) ", 4),
Test("min(1.0, two) + max(one, 2.0)", 3.0, oneTwo),
Test("min(1.0, two) + max(one, 2.0)", 3.0, OneTwo),
Test("sub(2, 1)", 1),
Test("sub(2, 1, 1)", 0),
Test("sub(2.0, 0.5)", 1.5),
@ -463,11 +462,11 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("rand(2, 3)", 2),
Test("range(1,4)", new[] { 1, 2, 3, 4 }),
Test("range(-1,6)", new[] { -1, 0, 1, 2, 3, 4 }),
# endregion
#endregion
# region Date and time function test
#region Date and time function test
//init dateTime: 2018-03-15T13:00:00Z
// init dateTime: 2018-03-15T13:00:00Z
Test("addDays(timestamp, 1)", "2018-03-16T13:00:00.000Z"),
Test("addDays(timestamp, 1,'MM-dd-yy')", "03-16-18"),
Test("addHours(timestamp, 1)", "2018-03-15T14:00:00.000Z"),
@ -477,10 +476,10 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("addSeconds(timestamp, 1)", "2018-03-15T13:00:01.000Z"),
Test("addSeconds(timestamp, 1, 'MM-dd-yy hh-mm-ss')", "03-15-18 01-00-01"),
Test("dayOfMonth(timestamp)", 15),
Test("dayOfWeek(timestamp)", 4), //Thursday
Test("dayOfWeek(timestamp)", 4), // Thursday
Test("dayOfYear(timestamp)", 74),
Test("month(timestamp)", 3),
Test("date(timestamp)", "3/15/2018"), //Default. TODO
Test("date(timestamp)", "3/15/2018"), // Default. TODO
Test("year(timestamp)", 2018),
Test("length(utcNow())", 24),
Test("utcNow('MM-DD-YY')", DateTime.UtcNow.ToString("MM-DD-YY")),
@ -584,9 +583,9 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("lastIndexOf(newGuid(), '-')", 23),
Test("lastIndexOf(hello, '-')", -1),
Test("length(newGuid())", 36),
# endregion
#endregion
# region Object manipulation and construction functions
#region Object manipulation and construction functions
Test("string(addProperty(json('{\"key1\":\"value1\"}'), 'key2','value2'))", "{\"key1\":\"value1\",\"key2\":\"value2\"}"),
Test("string(setProperty(json('{\"key1\":\"value1\"}'), 'key1','value2'))", "{\"key1\":\"value2\"}"),
Test("string(removeProperty(json('{\"key1\":\"value1\",\"key2\":\"value2\"}'), 'key2'))", "{\"key1\":\"value1\"}"),
@ -595,9 +594,9 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("xPath(xmlStr,'sum(/produce/item/count)')", 30),
Test("jPath(jsonStr,'Manufacturers[0].Products[0].Price')", 50),
Test("jPath(jsonStr,'$..Products[?(@.Price >= 50)].Name')", new[] { "Anvil", "Elbow Grease" }),
# endregion
#endregion
# region Short Hand Expression
#region Short Hand Expression
Test("@city == 'Bellevue'", false, new HashSet<string> { "turn.recognized.entities.city" }),
Test("@city", "Seattle", new HashSet<string> { "turn.recognized.entities.city" }),
Test("@city == 'Seattle'", true, new HashSet<string> { "turn.recognized.entities.city" }),
@ -644,7 +643,7 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("(getProperty(null, 'p'))[1]", null),
#endregion
# region Dialog
#region Dialog
Test("user.lists.todo[int(@@ordinal[0]) - 1] != null", true),
Test("user.lists.todo[int(@@ordinal[0]) + 3] != null", false),
Test("count(user.lists.todo) > int(@@ordinal[0]))", true),
@ -653,7 +652,7 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("user.lists[user.listType][int(@@ordinal[0]) - 1]", "todo1"),
#endregion
# region Regex
#region Regex
Test("isMatch('abc', '^[ab]+$')", false), // simple character classes ([abc]), "+" (one or more)
Test("isMatch('abb', '^[ab]+$')", true), // simple character classes ([abc])
Test("isMatch('123', '^[^abc]+$')", true), // complemented character classes ([^abc])
@ -678,10 +677,25 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("isMatch('12abc', '([0-9]+)([a-z]+)([0-9]+)')", false), // "(...)" (simple group)
Test(@"isMatch('a', '\\w{1}')", true), // "\w" (match [a-zA-Z0-9_])
Test(@"isMatch('1', '\\d{1}')", true), // "\d" (match [0-9])
# endregion
#endregion
};
[DataTestMethod()]
public static object[] Test(string input, object value, HashSet<string> paths = null) => new object[] { input, value, paths };
public static bool IsNumber(object value) =>
value is sbyte
|| value is byte
|| value is short
|| value is ushort
|| value is int
|| value is uint
|| value is long
|| value is ulong
|| value is float
|| value is double
|| value is decimal;
[DataTestMethod]
[DynamicData(nameof(Data))]
public void Evaluate(string input, object expected, HashSet<string> expectedRefs)
{
@ -714,19 +728,6 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
}
}
public static bool IsNumber(object value) =>
value is sbyte
|| value is byte
|| value is short
|| value is ushort
|| value is int
|| value is uint
|| value is long
|| value is ulong
|| value is float
|| value is double
|| value is decimal;
private void AssertObjectEquals(object expected, object actual)
{
if (IsNumber(actual) && IsNumber(expected))

Просмотреть файл

@ -7,8 +7,6 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
[TestClass]
public class RewriteTests
{
public static object[] Test(string input, string expected) => new object[] { input, expected };
public static IEnumerable<object[]> Data => new[]
{
/*
@ -38,6 +36,19 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("!(ignore(bark == 3))", "ignore(bark != 3)")
};
public static object[] Test(string input, string expected) => new object[] { input, expected };
[DataTestMethod]
[DynamicData(nameof(Data))]
public void Evaluate(string input, string expected)
{
var parser = new ExpressionEngine(Lookup);
var original = parser.Parse(input);
var dnf = original.DisjunctiveNormalForm();
var expectedDnf = parser.Parse(expected);
Assert.IsTrue(dnf.DeepEquals(expectedDnf), $"{original} is {dnf}, not {expectedDnf}");
}
private ExpressionEvaluator Lookup(string type)
{
ExpressionEvaluator eval;
@ -53,16 +64,5 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
return eval;
}
[DataTestMethod]
[DynamicData(nameof(Data))]
public void Evaluate(string input, string expected)
{
var parser = new ExpressionEngine(Lookup);
var original = parser.Parse(input);
var dnf = original.DisjunctiveNormalForm();
var expectedDnf = parser.Parse(expected);
Assert.IsTrue(dnf.DeepEquals(expectedDnf), $"{original} is {dnf}, not {expectedDnf}");
}
}
}

Просмотреть файл

@ -20,10 +20,10 @@ namespace Microsoft.Bot.Builder.AI.LanguageGeneration.Tests
[TestClass]
public class LGGeneratorTests
{
private readonly ImportResolverDelegate resourceResolver = LanguageGeneratorManager.ResourceResolver(resourceExplorer);
private static ResourceExplorer resourceExplorer;
private readonly ImportResolverDelegate resourceResolver = LanguageGeneratorManager.ResourceResolver(resourceExplorer);
public TestContext TestContext { get; set; }
[ClassInitialize]

Просмотреть файл

@ -9,16 +9,6 @@ namespace Microsoft.Bot.Builder.AI.LanguageGeneration.Tests
[TestClass]
public class TemplateEngineThrowExceptionTest
{
/// <summary>
/// Gets or sets the test context which provides
/// information about and functionality for the current test run.
/// </summary>
/// <value>
/// The test context which provides
/// information about and functionality for the current test run.
/// </value>
public TestContext TestContext { get; set; }
public static IEnumerable<object[]> StaticCheckExceptionData => new[]
{
Test("EmptyTemplate.lg"),
@ -60,6 +50,16 @@ namespace Microsoft.Bot.Builder.AI.LanguageGeneration.Tests
TestTemplate("LoopDetected.lg", "NotExistTemplate"),
};
/// <summary>
/// Gets or sets the test context which provides
/// information about and functionality for the current test run.
/// </summary>
/// <value>
/// The test context which provides
/// information about and functionality for the current test run.
/// </value>
public TestContext TestContext { get; set; }
public static object[] Test(string input) => new object[] { input };
public static object[] TestTemplate(string input, string templateName) => new object[] { input, templateName };