From 5bbcec092af61165a47d753317e789588c09fe32 Mon Sep 17 00:00:00 2001 From: Angelo Ribeiro Date: Tue, 29 Jan 2019 11:51:13 -0800 Subject: [PATCH] Adding E2E Test for Twin Operations. (#754) * Adding E2E Test for Twin Operations. * Fixing StyleCop Issues. * Addressing Code Review Comments. * Fixing Code Style. * Adressing more code review comments. * Passing Code Style. --- e2e_test_files/twin_test_tempSensor.json | 14 ++++++ smoke/IotEdgeQuickstart/Program.cs | 6 +++ smoke/IotEdgeQuickstart/Quickstart.cs | 4 +- smoke/IotEdgeQuickstart/details/Details.cs | 45 +++++++++++++++++++ .../details/TwinTestConfiguration.cs | 16 +++++++ 5 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 e2e_test_files/twin_test_tempSensor.json create mode 100644 smoke/IotEdgeQuickstart/details/TwinTestConfiguration.cs diff --git a/e2e_test_files/twin_test_tempSensor.json b/e2e_test_files/twin_test_tempSensor.json new file mode 100644 index 0000000000..ba8ea155fb --- /dev/null +++ b/e2e_test_files/twin_test_tempSensor.json @@ -0,0 +1,14 @@ +{ + "moduleId" : "tempSensor", + "properties" : { + "desired" : { + "SendInterval": 42, + "SendData": true, + "OtherType" : "test" + }, + "reported" : { + "SendInterval": 42, + "SendData": true + } + } +} \ No newline at end of file diff --git a/smoke/IotEdgeQuickstart/Program.cs b/smoke/IotEdgeQuickstart/Program.cs index 8d6ab40ef2..63b3fe5bc5 100644 --- a/smoke/IotEdgeQuickstart/Program.cs +++ b/smoke/IotEdgeQuickstart/Program.cs @@ -114,6 +114,9 @@ Defaults: [Option("-l|--deployment ", Description = "Deployment json file")] public string DeploymentFileName { get; } = Environment.GetEnvironmentVariable("deployment"); + [Option("-tw|--twin_test ", Description = "A file with Json content to set desired property and check reported property in a module.")] + public string TwinTestFileName { get; } = null; + [Option("--device_ca_cert", Description = "path to the device ca certificate and its chain")] public string DeviceCaCert { get; } = string.Empty; @@ -197,6 +200,8 @@ Defaults: Option deployment = this.DeploymentFileName != null ? Option.Some(this.DeploymentFileName) : Option.None(); + Option twinTest = this.TwinTestFileName != null ? Option.Some(this.TwinTestFileName) : Option.None(); + string tag = this.ImageTag ?? "1.0"; var test = new Quickstart( @@ -213,6 +218,7 @@ Defaults: this.NoVerify, this.VerifyDataFromModule, deployment, + twinTest, this.DeviceCaCert, this.DeviceCaPk, this.DeviceCaCerts, diff --git a/smoke/IotEdgeQuickstart/Quickstart.cs b/smoke/IotEdgeQuickstart/Quickstart.cs index f7865f758a..9b40657c90 100644 --- a/smoke/IotEdgeQuickstart/Quickstart.cs +++ b/smoke/IotEdgeQuickstart/Quickstart.cs @@ -27,13 +27,14 @@ namespace IotEdgeQuickstart bool noVerify, string verifyDataFromModule, Option deploymentFileName, + Option twinTestFileName, string deviceCaCert, string deviceCaPk, string deviceCaCerts, bool optimizedForPerformance, LogLevel runtimeLogLevel, bool cleanUpExistingDeviceOnSuccess) - : base(bootstrapper, credentials, iothubConnectionString, eventhubCompatibleEndpointWithEntityPath, upstreamProtocol, imageTag, deviceId, hostname, deploymentFileName, deviceCaCert, deviceCaPk, deviceCaCerts, optimizedForPerformance, runtimeLogLevel, cleanUpExistingDeviceOnSuccess) + : base(bootstrapper, credentials, iothubConnectionString, eventhubCompatibleEndpointWithEntityPath, upstreamProtocol, imageTag, deviceId, hostname, deploymentFileName, twinTestFileName, deviceCaCert, deviceCaPk, deviceCaCerts, optimizedForPerformance, runtimeLogLevel, cleanUpExistingDeviceOnSuccess) { this.leaveRunning = leaveRunning; this.noDeployment = noDeployment; @@ -67,6 +68,7 @@ namespace IotEdgeQuickstart if (!this.noVerify) { await this.VerifyDataOnIoTHub(this.verifyDataFromModule); + await this.VerifyTwinAsync(); } if (this.leaveRunning == LeaveRunning.Core) diff --git a/smoke/IotEdgeQuickstart/details/Details.cs b/smoke/IotEdgeQuickstart/details/Details.cs index 72ed6b7afc..d72dd7faf6 100644 --- a/smoke/IotEdgeQuickstart/details/Details.cs +++ b/smoke/IotEdgeQuickstart/details/Details.cs @@ -4,6 +4,7 @@ namespace IotEdgeQuickstart.Details using System; using System.Collections.Generic; using System.IO; + using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -21,6 +22,8 @@ namespace IotEdgeQuickstart.Details { public readonly Option DeploymentFileName; + public readonly Option TwinTestFileName; + const string DeployJson = @" { ""modulesContent"": { @@ -138,6 +141,7 @@ namespace IotEdgeQuickstart.Details string deviceId, string hostname, Option deploymentFileName, + Option twinTestFileName, string deviceCaCert, string deviceCaPk, string deviceCaCerts, @@ -172,6 +176,7 @@ namespace IotEdgeQuickstart.Details this.deviceId = deviceId; this.hostname = hostname; this.DeploymentFileName = deploymentFileName; + this.TwinTestFileName = twinTestFileName; this.deviceCaCert = deviceCaCert; this.deviceCaPk = deviceCaPk; this.deviceCaCerts = deviceCaCerts; @@ -334,6 +339,46 @@ namespace IotEdgeQuickstart.Details await eventHubClient.CloseAsync(); } + protected async Task VerifyTwinAsync() + { + await this.TwinTestFileName.ForEachAsync( + async fileName => + { + string twinTestJson = File.ReadAllText(fileName); + + var twinTest = JsonConvert.DeserializeObject(twinTestJson); + + Twin currentTwin = await this.context.RegistryManager.GetTwinAsync(this.context.Device.Id, twinTest.ModuleId); + + if (twinTest.Properties?.Desired?.Count > 0) + { + // Build Patch Object. + string patch = JsonConvert.SerializeObject(twinTest, Formatting.Indented); + await this.context.RegistryManager.UpdateTwinAsync(this.context.Device.Id, twinTest.ModuleId, patch, currentTwin.ETag); + } + + if (twinTest.Properties?.Reported?.Count > 0) + { + TimeSpan retryInterval = TimeSpan.FromSeconds(10); + bool IsValid(TwinCollection currentTwinReportedProperty) => twinTest.Properties.Reported.Cast>().All(p => currentTwinReportedProperty.Cast>().Contains(p)); + + using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20))) + { + async Task Func() + { + // Removing reSharper warning for CTS, Code Block will never exit before the delegate code completes because of using. + // ReSharper disable AccessToDisposedClosure + currentTwin = await this.context.RegistryManager.GetTwinAsync(this.context.Device.Id, twinTest.ModuleId, cts.Token); + // ReSharper restore AccessToDisposedClosure + return await Task.FromResult(currentTwin.Properties.Reported); + } + + await Retry.Do(Func, IsValid, null, retryInterval, cts.Token); + } + } + }); + } + protected Task RemoveTempSensorFromEdgeDevice() { (string deployJson, string[] _) = this.DeploymentJson(); diff --git a/smoke/IotEdgeQuickstart/details/TwinTestConfiguration.cs b/smoke/IotEdgeQuickstart/details/TwinTestConfiguration.cs new file mode 100644 index 0000000000..2a0d21cd1c --- /dev/null +++ b/smoke/IotEdgeQuickstart/details/TwinTestConfiguration.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All rights reserved. +namespace IotEdgeQuickstart.Details +{ + using System.Collections.Generic; + using Microsoft.Azure.Devices.Shared; + using Newtonsoft.Json; + + public class TwinTestConfiguration + { + [JsonProperty(PropertyName = "moduleId")] + public string ModuleId { get; set; } + + [JsonProperty(PropertyName = "properties")] + public TwinProperties Properties { get; set; } + } +}