From ffe2a6dcffd1dcfd11b5acf1af6f2b807701b882 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 25 May 2016 18:11:59 +0200 Subject: [PATCH] Add a HttpTextWriter that can send logs over http. There is no public API to create raw sockets with watchOS, so we can't use Tcp directly, thus the need for using Http. --- NUnitLite/TouchRunner/HttpTextWriter.cs | 100 ++++++++++++++++++++++++ NUnitLite/TouchRunner/TouchOptions.cs | 8 +- NUnitLite/TouchRunner/TouchRunner.cs | 41 ++++++++-- 3 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 NUnitLite/TouchRunner/HttpTextWriter.cs diff --git a/NUnitLite/TouchRunner/HttpTextWriter.cs b/NUnitLite/TouchRunner/HttpTextWriter.cs new file mode 100644 index 0000000..05bf8a2 --- /dev/null +++ b/NUnitLite/TouchRunner/HttpTextWriter.cs @@ -0,0 +1,100 @@ +// HttpTextWriter.cs: Class to report test results using http requests +// +// Authors: +// Rolf Bjarne Kvinge +// +// Copyright 2016 Xamarin Inc. +// + +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +#if __UNIFIED__ +using Foundation; +#else +using MonoTouch.Foundation; +#endif + +namespace MonoTouch.NUnit { + class HttpTextWriter : TextWriter + { + public string HostName; + public int Port; + + TaskCompletionSource finished = new TaskCompletionSource (); + TaskCompletionSource closed = new TaskCompletionSource (); + StringBuilder log = new StringBuilder (); + + public Task FinishedTask { + get { + return finished.Task; + } + } + + public override Encoding Encoding { + get { + return Encoding.UTF8; + } + } + + public override void Close () + { + closed.SetResult (true); + Task.Run (async () => + { + await finished.Task; + base.Close (); + }); + } + + Task SendData (string action, string uploadData) + { + var url = NSUrl.FromString ("http://" + HostName + ":" + Port + "/" + action); + var request = new NSMutableUrlRequest (url); + request.HttpMethod = "POST"; + return NSUrlSession.SharedSession.CreateUploadTaskAsync (request, NSData.FromString (uploadData)); + } + + async void SendThread () + { + try { + await SendData ("Start", ""); + await closed.Task; + await SendData ("Finish", log.ToString ()); + } catch (Exception ex) { + Console.WriteLine ("HttpTextWriter failed: {0}", ex); + } finally { + finished.SetResult (true); + } + } + + public void Open () + { + new Thread (SendThread) + { + IsBackground = true, + }.Start (); + } + + public override void Write (char value) + { + Console.Out.Write (value); + log.Append (value); + } + + public override void Write (char [] buffer) + { + Console.Out.Write (buffer); + log.Append (buffer); + } + + public override void WriteLine (string value) + { + Console.Out.WriteLine (value); + log.AppendLine (value); + } + } +} diff --git a/NUnitLite/TouchRunner/TouchOptions.cs b/NUnitLite/TouchRunner/TouchOptions.cs index f667a0e..8395dd8 100644 --- a/NUnitLite/TouchRunner/TouchOptions.cs +++ b/NUnitLite/TouchRunner/TouchOptions.cs @@ -48,6 +48,7 @@ public TouchOptions () EnableNetwork = defaults.BoolForKey ("network.enabled"); HostName = defaults.StringForKey ("network.host.name"); HostPort = (int)defaults.IntForKey ("network.host.port"); + Transport = defaults.StringForKey ("network.transport"); SortNames = defaults.BoolForKey ("display.sort"); bool b; @@ -64,13 +65,16 @@ public TouchOptions () HostPort = i; if (bool.TryParse (Environment.GetEnvironmentVariable ("NUNIT_SORTNAMES"), out b)) SortNames = b; + if (!string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("NUNIT_TRANSPORT"))) + Transport = Environment.GetEnvironmentVariable ("NUNIT_TRANSPORT"); var os = new OptionSet () { { "autoexit", "If the app should exit once the test run has completed.", v => TerminateAfterExecution = true }, { "autostart", "If the app should automatically start running the tests.", v => AutoStart = true }, { "hostname=", "Comma-separated list of host names or IP address to (try to) connect to", v => HostName = v }, - { "hostport=", "TCP port to connect to.", v => HostPort = int.Parse (v) }, + { "hostport=", "HTTP/TCP port to connect to.", v => HostPort = int.Parse (v) }, { "enablenetwork", "Enable the network reporter.", v => EnableNetwork = true }, + { "transport=", "Select transport method. Either TCP (default) or HTTP.", v => Transport = v }, }; try { @@ -90,6 +94,8 @@ public TouchOptions () public bool TerminateAfterExecution { get; set; } + public string Transport { get; set; } = "TCP"; + public bool ShowUseNetworkLogger { get { return (EnableNetwork && !String.IsNullOrWhiteSpace (HostName) && (HostPort > 0)); } } diff --git a/NUnitLite/TouchRunner/TouchRunner.cs b/NUnitLite/TouchRunner/TouchRunner.cs index 5239b1d..7d811be 100644 --- a/NUnitLite/TouchRunner/TouchRunner.cs +++ b/NUnitLite/TouchRunner/TouchRunner.cs @@ -127,8 +127,16 @@ public void AutoRun () // optionally end the process, e.g. click "Touch.Unit" -> log tests results, return to springboard... // http://stackoverflow.com/questions/1978695/uiapplication-sharedapplication-terminatewithsuccess-is-not-there - if (TerminateAfterExecution) - TerminateWithSuccess (); + if (TerminateAfterExecution) { + if (WriterFinishedTask != null) { + Task.Run (async () => { + await WriterFinishedTask; + TerminateWithSuccess (); + }); + } else { + TerminateWithSuccess (); + } + } }); } @@ -149,6 +157,8 @@ public void Run () public TextWriter Writer { get; set; } + Task WriterFinishedTask { get; set; } + static string SelectHostName (string[] names, int port) { if (names.Length == 0) @@ -205,15 +215,34 @@ public bool OpenWriter (string message) if (hostname != null) { Console.WriteLine ("[{0}] Sending '{1}' results to {2}:{3}", now, message, hostname, options.HostPort); try { - Writer = new TcpTextWriter (hostname, options.HostPort); + WriterFinishedTask = null; + switch (options.Transport) { + case "HTTP": + var w = new HttpTextWriter () + { + HostName = hostname, + Port = options.HostPort, + }; + w.Open (); + Writer = w; + WriterFinishedTask = w.FinishedTask; + break; + default: + Console.WriteLine ("Unknown transport '{0}': switching to default (TCP)", options.Transport); + goto case "TCP"; + case "TCP": + Writer = new TcpTextWriter (hostname, options.HostPort); + break; + } } - catch (SocketException) { + catch (Exception ex) { #if __TVOS__ || __WATCHOS__ - Console.WriteLine ("Network error: Cannot connect to {0}:{1}. Continuing on console.", hostname, options.HostPort); + Console.WriteLine ("Network error: Cannot connect to {0}:{1}: {2}. Continuing on console.", hostname, options.HostPort, ex); Writer = Console.Out; #else + Console.WriteLine ("Network error: Cannot connect to {0}:{1}: {2}.", hostname, options.HostPort, ex); UIAlertView alert = new UIAlertView ("Network Error", - String.Format ("Cannot connect to {0}:{1}. Continue on console ?", hostname, options.HostPort), + String.Format ("Cannot connect to {0}:{1}: {2}. Continue on console ?", hostname, options.HostPort, ex.Message), null, "Cancel", "Continue"); int button = -1; alert.Clicked += delegate(object sender, UIButtonEventArgs e) {