Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/DynamoDS/Dynamo
Browse files Browse the repository at this point in the history
  • Loading branch information
zeusongit committed Dec 1, 2023
2 parents 71d23c2 + 7fdfc7f commit cd1c4f9
Show file tree
Hide file tree
Showing 12 changed files with 331 additions and 17 deletions.
5 changes: 4 additions & 1 deletion src/DynamoCoreWpf/Views/SplashScreen/SplashScreen.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,10 @@ protected override void OnClosed(EventArgs e)
DynamoModel.RequestUpdateLoadBarStatus -= DynamoModel_RequestUpdateLoadBarStatus;
DynamoModel.LanguageDetected -= DynamoModel_LanguageDetected;
StaticSplashScreenReady -= OnStaticScreenReady;
authManager.LoginStateChanged -= OnLoginStateChanged;
if (authManager is not null)
{
authManager.LoginStateChanged -= OnLoginStateChanged;
}
webView.Dispose();
webView = null;

Expand Down
17 changes: 14 additions & 3 deletions src/DynamoUtilities/CLIWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ protected static string GetToolPath(string relativePath)
/// Read data from CLI tool
/// </summary>
/// <param name="timeoutms">will return empty string if we don't finish reading all data in the timeout provided in milliseconds.</param>
/// <param name="mockReadLine"> if this delegate is non null, it will be used instead of communicating with std out of the process. Used for testing only.</param>
/// <returns></returns>
protected virtual async Task<string> GetData(int timeoutms)
protected virtual string GetData(int timeoutms, Func<string> mockReadLine = null)
{
var readStdOutTask = Task.Run(() =>
{
Expand All @@ -114,7 +115,17 @@ protected virtual async Task<string> GetData(int timeoutms)
{
try
{
var line = process.StandardOutput.ReadLine();
string line = null;
if(mockReadLine != null)
{
line = mockReadLine.Invoke();
}
else
{
line = process.StandardOutput.ReadLine();
}


MessageLogged?.Invoke(line);
if (line == null || line == startofDataToken)
{
Expand Down Expand Up @@ -145,7 +156,7 @@ protected virtual async Task<string> GetData(int timeoutms)
return writer.ToString();
}
});
var completedTask = await Task.WhenAny(readStdOutTask, Task.Delay(TimeSpan.FromMilliseconds(timeoutms)));
var completedTask = Task.WhenAny(readStdOutTask, Task.Delay(TimeSpan.FromMilliseconds(timeoutms))).Result;
//if the completed task was our read std out task, then return the data
//else we timed out, so return an empty string.
return completedTask == readStdOutTask ? readStdOutTask.Result : string.Empty;
Expand Down
2 changes: 1 addition & 1 deletion src/DynamoUtilities/DynamoFeatureFlagsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ internal void CacheAllFlags()
{

//wait for response
var dataFromCLI = GetData(featureFlagTimeoutMs).Result;
var dataFromCLI = GetData(featureFlagTimeoutMs);
//convert from json string to dictionary.
try
{
Expand Down
8 changes: 2 additions & 6 deletions src/DynamoUtilities/Md2Html.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,7 @@ internal string ParseMd2Html(string mdString, string mdPath)
return GetCantCommunicateErrorMessage();
}

var output = GetData(processCommunicationTimeoutms);

return output.Result;
return GetData(processCommunicationTimeoutms);
}

/// <summary>
Expand All @@ -102,9 +100,7 @@ internal string SanitizeHtml(string content)
return GetCantCommunicateErrorMessage();
}

var output = GetData(processCommunicationTimeoutms);

return output.Result;
return GetData(processCommunicationTimeoutms);
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions src/Libraries/CoreNodeModels/Logic/Gate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ namespace CoreNodeModels.Logic
[NodeSearchTags(nameof(Resources.GateSearchTags), typeof(Resources))]
[InPortNames(">")]
[InPortTypes("object")]
[InPortDescriptions(nameof(Resources.GateInPortToolTip), nameof(Resources))]
[InPortDescriptions(typeof(Resources), nameof(Resources.GateInPortToolTip))]
[OutPortNames(">")]
[OutPortTypes("object")]
[OutPortDescriptions(nameof(Resources.GateOutPortToolTip), nameof(Resources))]
[OutPortDescriptions(typeof(Resources), nameof(Resources.GateOutPortToolTip))]
[IsDesignScriptCompatible]
public class Gate : NodeModel
{
Expand Down
66 changes: 63 additions & 3 deletions src/Libraries/CoreNodes/Data.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
using Dynamo.Events;
using Dynamo.Logging;
using Dynamo.Session;
using System.Globalization;
using System.Text;

namespace DSCore
{
Expand Down Expand Up @@ -188,8 +190,9 @@ private static object DynamoJObjectToNative(JObject jObject)
/// <returns name="json">A JSON string where primitive types (e.g. double, int, boolean), Lists, and Dictionary's will be turned into the associated JSON type.</returns>
public static string StringifyJSON([ArbitraryDimensionArrayImport] object values)
{
return JsonConvert.SerializeObject(values,
new JsonConverter[]
var settings = new JsonSerializerSettings()
{
Converters = new JsonConverter[]
{
new DictConverter(),
new DesignScriptGeometryConverter(),
Expand All @@ -198,9 +201,66 @@ public static string StringifyJSON([ArbitraryDimensionArrayImport] object values
#if _WINDOWS
new PNGImageConverter(),
#endif
});
}
};

StringBuilder sb = new StringBuilder(256);
using (var writer = new StringWriter(sb, CultureInfo.InvariantCulture))
{
using (var jsonWriter = new MaxDepthJsonTextWriter(writer))
{
JsonSerializer.Create(settings).Serialize(jsonWriter, values);
}
return writer.ToString();
}
}

/// <summary>
/// Subclass of JsonTextWriter that limits a maximum supported object depth to prevent circular reference crashes when serializing arbitrary .NET objects types.
/// </summary>
private class MaxDepthJsonTextWriter : JsonTextWriter
{
private readonly int maxDepth = 15;
private int depth = 0;

public MaxDepthJsonTextWriter(TextWriter writer) : base(writer) { }

public override void WriteStartArray()
{
base.WriteStartArray();
depth++;
CheckDepth();
}

public override void WriteEndArray()
{
base.WriteEndArray();
depth--;
CheckDepth();
}

public override void WriteStartObject()
{
base.WriteStartObject();
depth++;
CheckDepth();
}

public override void WriteEndObject()
{
base.WriteEndObject();
depth--;
CheckDepth();
}

private void CheckDepth()
{
if (depth > maxDepth)
{
throw new JsonSerializationException(string.Format(Properties.Resources.Exception_Serialize_Depth_Unsupported, depth, maxDepth, Path));
}
}
}

#region Converters
/// <summary>
Expand Down
9 changes: 9 additions & 0 deletions src/Libraries/CoreNodes/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Libraries/CoreNodes/Properties/Resources.en-US.resx
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@
<data name="Exception_Deserialize_Unsupported_Cache" xml:space="preserve">
<value>The stored data can not be loaded.</value>
</data>
<data name="Exception_Serialize_Depth_Unsupported" xml:space="preserve">
<value>Depth {0} Exceeds MaxDepth {1} at path "{2}"</value>
</data>
<data name="Exception_Serialize_DesignScript_Unsupported" xml:space="preserve">
<value>This type of Geometry is not able to be serialized.</value>
</data>
Expand Down
3 changes: 3 additions & 0 deletions src/Libraries/CoreNodes/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@
<data name="Exception_Deserialize_Unsupported_Cache" xml:space="preserve">
<value>The stored data can not be loaded.</value>
</data>
<data name="Exception_Serialize_Depth_Unsupported" xml:space="preserve">
<value>Depth {0} Exceeds MaxDepth {1} at path "{2}"</value>
</data>
<data name="Exception_Serialize_DesignScript_Unsupported" xml:space="preserve">
<value>This type of Geometry is not able to be serialized.</value>
</data>
Expand Down
20 changes: 20 additions & 0 deletions test/DynamoCoreTests/DSCoreDataTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using System.Text;
using Dynamo.Graph.Nodes;
using Dynamo.Graph.Nodes.ZeroTouch;
using DynamoUnits;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
Expand Down Expand Up @@ -192,6 +193,25 @@ public void ParsingJSONInPythonReturnsSameResult()
AssertPreviewValue("cdad5bf1-f5f7-47f4-a119-ad42e5084cfa", true);
}

[Test]
[Category("UnitTests")]
public void SerializingObjectOverMaximumDepthFailes()
{
// Load test graph
string path = Path.Combine(TestDirectory, @"core\json\JSON_Serialization_Depth_Fail.dyn");
OpenModel(path);

var node = CurrentDynamoModel.CurrentWorkspace.NodeFromWorkspace<DSFunction>(
Guid.Parse("cc45bec3172e40dab4d967e9dd81cbdd"));

var expectedWarning = "Exceeds MaxDepth";

Assert.AreEqual(node.State, ElementState.Warning);
AssertPreviewValue("cc45bec3172e40dab4d967e9dd81cbdd", null);
Assert.AreEqual(node.Infos.Count, 1);
Assert.IsTrue(node.Infos.Any(x => x.Message.Contains(expectedWarning) && x.State == ElementState.Warning));
}

[Test]
[Category("UnitTests")]
public void RoundTripForBoundingBoxReturnsSameResult()
Expand Down
64 changes: 63 additions & 1 deletion test/Libraries/DynamoUtilitiesTests/CLIWrapperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;

Expand Down Expand Up @@ -39,7 +40,46 @@ internal string GetData()
{ System.Threading.Thread.Sleep(100);
process.Kill();
});
return GetData(2000).Result;
return GetData(2000);
}


}
/// <summary>
/// this test class waits before reading from the console, so GetData is slow.
/// </summary>
private class SlowCLIWrapper : HangingCLIWrapper
{
internal new string GetData()
{
return GetData(2000, () => { Thread.Sleep(4000);return ""; });
}
}

/// <summary>
/// this test class should get mock data and should not time out.
/// </summary>
private class MockCLIWraper : HangingCLIWrapper
{
int count = 0;
internal new string GetData()
{
return GetData(2000, () => {
count++;

switch (count)
{
case 1:
return startofDataToken;
case 2:
return "some data";
case 3:
return endOfDataToken;
default:
return string.Empty;
}

});
}
}

Expand All @@ -54,5 +94,27 @@ public void CLIWrapperDoesNotHangIfProcessDoesNotWriteToStdOut()
Assert.GreaterOrEqual(sw.ElapsedMilliseconds,2000);

}
[Test]
public void CLIWrapperTimesOutIfGetDataIsSlow()
{
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
var wrapper = new SlowCLIWrapper();
Assert.AreEqual(string.Empty, wrapper.GetData());
sw.Stop();
Assert.GreaterOrEqual(sw.ElapsedMilliseconds, 2000);

}
[Test]
public void CLIGetsDataIfDoesNotTimeout()
{
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
var wrapper = new MockCLIWraper();
Assert.AreEqual("some data", wrapper.GetData().TrimEnd());
sw.Stop();
Assert.LessOrEqual(sw.ElapsedMilliseconds, 2000);

}
}
}
Loading

0 comments on commit cd1c4f9

Please sign in to comment.