Initial check-in of Xamarin runner apps
This commit is contained in:
Коммит
416ed4ec4d
|
@ -0,0 +1,28 @@
|
|||
*.ico binary
|
||||
*.snk binary
|
||||
*.xls binary
|
||||
|
||||
*.bat text
|
||||
*.config text
|
||||
*.cs text diff=csharp
|
||||
*.csproj text merge=union
|
||||
*.manifest text
|
||||
*.msbuild text
|
||||
*.nuspec text
|
||||
*.resx text merge=union
|
||||
*.ruleset text
|
||||
*.settings text
|
||||
*.shfb text
|
||||
*.targets text
|
||||
*.tdnet text
|
||||
*.txt text
|
||||
*.vb text
|
||||
*.vbproj text merge=union
|
||||
*.vsixmanifest text
|
||||
*.vstemplate text
|
||||
*.xml text
|
||||
*.xsl text
|
||||
*.xslt text
|
||||
*.xunit text
|
||||
|
||||
*.sln text eol=crlf merge=union
|
|
@ -0,0 +1,23 @@
|
|||
[Bb]in
|
||||
[Oo]bj
|
||||
packages
|
||||
help
|
||||
|
||||
*.suo
|
||||
*.user
|
||||
*.[Cc]ache
|
||||
*[Rr]esharper*
|
||||
*.zip
|
||||
*.suo
|
||||
*.user
|
||||
*.cache
|
||||
*.nupkg
|
||||
*.exe
|
||||
*.dll
|
||||
*.ncrunch*
|
||||
|
||||
Test*.html
|
||||
Test*.xml
|
||||
|
||||
Index.dat
|
||||
Storage.dat
|
|
@ -0,0 +1,13 @@
|
|||
Copyright 2014 Outercurve Foundation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Dictionary>
|
||||
<Words>
|
||||
<Recognized>
|
||||
<Word>xunit</Word>
|
||||
</Recognized>
|
||||
</Words>
|
||||
</Dictionary>
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 266 KiB |
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
internal static class AssemblyExtensions
|
||||
{
|
||||
public static string GetLocalCodeBase(this Assembly assembly)
|
||||
{
|
||||
string codeBase = assembly.CodeBase;
|
||||
if (codeBase == null)
|
||||
return null;
|
||||
|
||||
if (!codeBase.StartsWith("file:///"))
|
||||
throw new ArgumentException(String.Format("Code base {0} in wrong format; must start with file:///", codeBase), "assembly");
|
||||
|
||||
codeBase = codeBase.Substring(8);
|
||||
if (Path.DirectorySeparatorChar == '/')
|
||||
return "/" + codeBase;
|
||||
|
||||
return codeBase.Replace('/', Path.DirectorySeparatorChar);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
internal static class DictionaryExtensions
|
||||
{
|
||||
public static void Add<TKey, TValue>(this IDictionary<TKey, List<TValue>> dictionary, TKey key, TValue value)
|
||||
{
|
||||
dictionary.GetOrAdd(key).Add(value);
|
||||
}
|
||||
|
||||
public static bool Contains<TKey, TValue>(this IDictionary<TKey, List<TValue>> dictionary, TKey key, TValue value, IEqualityComparer<TValue> valueComparer)
|
||||
{
|
||||
List<TValue> values;
|
||||
|
||||
if (!dictionary.TryGetValue(key, out values))
|
||||
return false;
|
||||
|
||||
return values.Contains(value, valueComparer);
|
||||
}
|
||||
|
||||
public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
|
||||
where TValue : new()
|
||||
{
|
||||
return dictionary.GetOrAdd<TKey, TValue>(key, () => new TValue());
|
||||
}
|
||||
|
||||
public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TValue> newValue)
|
||||
{
|
||||
TValue result;
|
||||
|
||||
if (!dictionary.TryGetValue(key, out result))
|
||||
{
|
||||
result = newValue();
|
||||
dictionary[key] = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
|
||||
internal static class DisposableExtensions
|
||||
{
|
||||
public static void SafeDispose(this IDisposable disposable)
|
||||
{
|
||||
if (disposable != null)
|
||||
disposable.Dispose();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
internal static class ExceptionExtensions
|
||||
{
|
||||
const string RETHROW_MARKER = "$$RethrowMarker$$";
|
||||
|
||||
/// <summary>
|
||||
/// Rethrows an exception object without losing the existing stack trace information
|
||||
/// </summary>
|
||||
/// <param name="ex">The exception to re-throw.</param>
|
||||
/// <remarks>
|
||||
/// For more information on this technique, see
|
||||
/// http://www.dotnetjunkies.com/WebLog/chris.taylor/archive/2004/03/03/8353.aspx.
|
||||
/// The remote_stack_trace string is here to support Mono.
|
||||
/// </remarks>
|
||||
public static void RethrowWithNoStackTraceLoss(this Exception ex)
|
||||
{
|
||||
#if XUNIT_CORE_DLL
|
||||
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw();
|
||||
#else
|
||||
FieldInfo remoteStackTraceString =
|
||||
typeof(Exception).GetField("_remoteStackTraceString", BindingFlags.Instance | BindingFlags.NonPublic) ??
|
||||
typeof(Exception).GetField("remote_stack_trace", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
remoteStackTraceString.SetValue(ex, ex.StackTrace + RETHROW_MARKER);
|
||||
throw ex;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unwraps an exception to remove any wrappers, like <see cref="TargetInvocationException"/>.
|
||||
/// </summary>
|
||||
/// <param name="ex">The exception to unwrap.</param>
|
||||
/// <returns>The unwrapped exception.</returns>
|
||||
public static Exception Unwrap(this Exception ex)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var tiex = ex as TargetInvocationException;
|
||||
if (tiex == null)
|
||||
return ex;
|
||||
|
||||
ex = tiex.InnerException;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyCompany("Outercurve Foundation")]
|
||||
[assembly: AssemblyProduct("xUnit.net Testing Framework")]
|
||||
[assembly: AssemblyCopyright("Copyright (C) Outercurve Foundation")]
|
||||
[assembly: AssemblyVersion("2.0.0.0")]
|
||||
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Xunit.Sdk")]
|
||||
[assembly: SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "xunit")]
|
||||
[assembly: SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "extensions")]
|
||||
[assembly: SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "utility")]
|
||||
[assembly: SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "runner")]
|
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Guard class, used for guard clauses and argument validation
|
||||
/// </summary>
|
||||
internal static class Guard
|
||||
{
|
||||
/// <summary/>
|
||||
public static void ArgumentNotNull(string argName, object argValue)
|
||||
{
|
||||
if (argValue == null)
|
||||
throw new ArgumentNullException(argName);
|
||||
}
|
||||
|
||||
/// <summary/>
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method may not be called by all users of Guard.")]
|
||||
public static void ArgumentNotNullOrEmpty(string argName, IEnumerable argValue)
|
||||
{
|
||||
ArgumentNotNull(argName, argValue);
|
||||
|
||||
if (!argValue.GetEnumerator().MoveNext())
|
||||
throw new ArgumentException("Argument was empty", argName);
|
||||
}
|
||||
|
||||
/// <summary/>
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method may not be called by all users of Guard.")]
|
||||
public static void ArgumentValid(string argName, string message, bool test)
|
||||
{
|
||||
if (!test)
|
||||
throw new ArgumentException(message, argName);
|
||||
}
|
||||
|
||||
#if !XUNIT_CORE_DLL
|
||||
/// <summary/>
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method may not be called by all users of Guard.")]
|
||||
public static void FileExists(string argName, string fileName)
|
||||
{
|
||||
Guard.ArgumentNotNullOrEmpty(argName, fileName);
|
||||
Guard.ArgumentValid("assemblyFileName",
|
||||
String.Format("File not found: {0}", fileName),
|
||||
File.Exists(fileName));
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
using System.Runtime.Serialization;
|
||||
|
||||
internal static class SerializationInfoExtensions
|
||||
{
|
||||
public static T GetValue<T>(this SerializationInfo info, string name)
|
||||
{
|
||||
return (T)info.GetValue(name, typeof(T));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
#if XUNIT_CORE_DLL
|
||||
namespace Xunit.Sdk
|
||||
#else
|
||||
namespace Xunit
|
||||
#endif
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of <see cref="ISourceInformation"/>.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class SourceInformation : LongLivedMarshalByRefObject, ISourceInformation, ISerializable
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SourceInformation"/> class.
|
||||
/// </summary>
|
||||
public SourceInformation() { }
|
||||
|
||||
/// <summary/>
|
||||
protected SourceInformation(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
FileName = info.GetString("FileName");
|
||||
LineNumber = (int?)info.GetValue("LineNumber", typeof(int?));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string FileName { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int? LineNumber { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
info.AddValue("FileName", FileName);
|
||||
info.AddValue("LineNumber", LineNumber, typeof(int?));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using System.Collections.Generic;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Xunit
|
||||
{
|
||||
internal class TestDiscoveryVisitor : TestMessageVisitor<IDiscoveryCompleteMessage>
|
||||
{
|
||||
public TestDiscoveryVisitor()
|
||||
{
|
||||
TestCases = new List<ITestCase>();
|
||||
}
|
||||
|
||||
public List<ITestCase> TestCases { get; private set; }
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
TestCases.ForEach(testCase => testCase.Dispose());
|
||||
TestCases = null;
|
||||
}
|
||||
|
||||
protected override bool Visit(ITestCaseDiscoveryMessage discovery)
|
||||
{
|
||||
TestCases.Add(discovery.TestCase);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
internal static class TestOptionsNames
|
||||
{
|
||||
internal static class Discovery
|
||||
{
|
||||
}
|
||||
|
||||
internal static class Execution
|
||||
{
|
||||
public static readonly string SynchronousMessageReporting = "xunit.SynchronousMessageReporting";
|
||||
public static readonly string DisableParallelization = "xunit.DisableParallelization";
|
||||
public static readonly string MaxParallelThreads = "xunit.MaxParallelThreads";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Xml.Linq;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Xunit
|
||||
{
|
||||
public class XmlTestExecutionVisitor : TestMessageVisitor<ITestAssemblyFinished>
|
||||
{
|
||||
readonly XElement assemblyElement;
|
||||
readonly ConcurrentDictionary<ITestCollection, XElement> testCollectionElements = new ConcurrentDictionary<ITestCollection, XElement>();
|
||||
|
||||
public XmlTestExecutionVisitor(XElement assemblyElement, Func<bool> cancelThunk)
|
||||
{
|
||||
CancelThunk = cancelThunk ?? (() => false);
|
||||
|
||||
this.assemblyElement = assemblyElement;
|
||||
}
|
||||
|
||||
public readonly Func<bool> CancelThunk;
|
||||
public int Failed;
|
||||
public int Skipped;
|
||||
public decimal Time;
|
||||
public int Total;
|
||||
|
||||
XElement CreateTestResultElement(ITestResultMessage testResult, string resultText)
|
||||
{
|
||||
var collectionElement = GetTestCollectionElement(testResult.TestCase.TestCollection);
|
||||
var testResultElement =
|
||||
new XElement("test",
|
||||
new XAttribute("name", XmlEscape(testResult.TestDisplayName)),
|
||||
new XAttribute("type", testResult.TestCase.Class.Name),
|
||||
new XAttribute("method", testResult.TestCase.Method.Name),
|
||||
new XAttribute("time", testResult.ExecutionTime.ToString("0.000")),
|
||||
new XAttribute("result", resultText)
|
||||
);
|
||||
|
||||
if (testResult.TestCase.SourceInformation != null)
|
||||
{
|
||||
if (testResult.TestCase.SourceInformation.FileName != null)
|
||||
testResultElement.Add(new XAttribute("source-file", testResult.TestCase.SourceInformation.FileName));
|
||||
if (testResult.TestCase.SourceInformation.LineNumber != null)
|
||||
testResultElement.Add(new XAttribute("source-line", testResult.TestCase.SourceInformation.LineNumber.GetValueOrDefault()));
|
||||
}
|
||||
|
||||
if (testResult.TestCase.Traits != null && testResult.TestCase.Traits.Count > 0)
|
||||
{
|
||||
var traitsElement = new XElement("traits");
|
||||
|
||||
foreach (var key in testResult.TestCase.Traits.Keys)
|
||||
foreach (var value in testResult.TestCase.Traits[key])
|
||||
traitsElement.Add(
|
||||
new XElement("trait",
|
||||
new XAttribute("name", XmlEscape(key)),
|
||||
new XAttribute("value", XmlEscape(value))
|
||||
)
|
||||
);
|
||||
|
||||
testResultElement.Add(traitsElement);
|
||||
}
|
||||
|
||||
collectionElement.Add(testResultElement);
|
||||
|
||||
return testResultElement;
|
||||
}
|
||||
|
||||
XElement GetTestCollectionElement(ITestCollection testCollection)
|
||||
{
|
||||
return testCollectionElements.GetOrAdd(testCollection, tc => new XElement("collection"));
|
||||
}
|
||||
|
||||
public override bool OnMessage(IMessageSinkMessage message)
|
||||
{
|
||||
var result = base.OnMessage(message);
|
||||
if (result)
|
||||
result = !CancelThunk();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override bool Visit(ITestAssemblyFinished assemblyFinished)
|
||||
{
|
||||
Total += assemblyFinished.TestsRun;
|
||||
Failed += assemblyFinished.TestsFailed;
|
||||
Skipped += assemblyFinished.TestsSkipped;
|
||||
Time += assemblyFinished.ExecutionTime;
|
||||
|
||||
if (assemblyElement != null)
|
||||
{
|
||||
assemblyElement.Add(
|
||||
new XAttribute("total", Total),
|
||||
new XAttribute("passed", Total - Failed - Skipped),
|
||||
new XAttribute("failed", Failed),
|
||||
new XAttribute("skipped", Skipped),
|
||||
new XAttribute("time", Time.ToString("0.000"))
|
||||
);
|
||||
|
||||
foreach (var element in testCollectionElements.Values)
|
||||
assemblyElement.Add(element);
|
||||
}
|
||||
|
||||
return base.Visit(assemblyFinished);
|
||||
}
|
||||
|
||||
protected override bool Visit(ITestAssemblyStarting assemblyStarting)
|
||||
{
|
||||
if (assemblyElement != null)
|
||||
{
|
||||
assemblyElement.Add(
|
||||
new XAttribute("name", assemblyStarting.AssemblyFileName),
|
||||
new XAttribute("environment", assemblyStarting.TestEnvironment),
|
||||
new XAttribute("test-framework", assemblyStarting.TestFrameworkDisplayName),
|
||||
new XAttribute("run-date", assemblyStarting.StartTime.ToString("yyyy-MM-dd")),
|
||||
new XAttribute("run-time", assemblyStarting.StartTime.ToString("HH:mm:ss"))
|
||||
);
|
||||
|
||||
if (assemblyStarting.ConfigFileName != null)
|
||||
assemblyElement.Add(new XAttribute("config-file", assemblyStarting.ConfigFileName));
|
||||
}
|
||||
|
||||
return base.Visit(assemblyStarting);
|
||||
}
|
||||
|
||||
protected override bool Visit(ITestCollectionFinished testCollectionFinished)
|
||||
{
|
||||
if (assemblyElement != null)
|
||||
{
|
||||
var collectionElement = GetTestCollectionElement(testCollectionFinished.TestCollection);
|
||||
collectionElement.Add(
|
||||
new XAttribute("total", testCollectionFinished.TestsRun),
|
||||
new XAttribute("passed", testCollectionFinished.TestsRun - testCollectionFinished.TestsFailed - testCollectionFinished.TestsSkipped),
|
||||
new XAttribute("failed", testCollectionFinished.TestsFailed),
|
||||
new XAttribute("skipped", testCollectionFinished.TestsSkipped),
|
||||
new XAttribute("name", XmlEscape(testCollectionFinished.TestCollection.DisplayName)),
|
||||
new XAttribute("time", testCollectionFinished.ExecutionTime.ToString("0.000"))
|
||||
);
|
||||
}
|
||||
|
||||
return base.Visit(testCollectionFinished);
|
||||
}
|
||||
|
||||
protected override bool Visit(ITestFailed testFailed)
|
||||
{
|
||||
if (assemblyElement != null)
|
||||
{
|
||||
var testElement = CreateTestResultElement(testFailed, "Fail");
|
||||
testElement.Add(
|
||||
new XElement("failure",
|
||||
new XAttribute("exception-type", testFailed.ExceptionTypes[0]),
|
||||
new XElement("message", new XCData(XmlEscape(ExceptionUtility.CombineMessages(testFailed)))),
|
||||
new XElement("stack-trace", new XCData(ExceptionUtility.CombineStackTraces(testFailed) ?? String.Empty))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return base.Visit(testFailed);
|
||||
}
|
||||
|
||||
protected override bool Visit(ITestPassed testPassed)
|
||||
{
|
||||
if (assemblyElement != null)
|
||||
CreateTestResultElement(testPassed, "Pass");
|
||||
|
||||
return base.Visit(testPassed);
|
||||
}
|
||||
|
||||
protected override bool Visit(ITestSkipped testSkipped)
|
||||
{
|
||||
if (assemblyElement != null)
|
||||
{
|
||||
var testElement = CreateTestResultElement(testSkipped, "Skip");
|
||||
testElement.Add(new XElement("reason", new XCData(XmlEscape(testSkipped.Reason))));
|
||||
}
|
||||
|
||||
return base.Visit(testSkipped);
|
||||
}
|
||||
|
||||
protected static string Escape(string value)
|
||||
{
|
||||
if (value == null)
|
||||
return String.Empty;
|
||||
|
||||
return value.Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t").Replace("\0", "\\0");
|
||||
}
|
||||
|
||||
protected static string XmlEscape(string value)
|
||||
{
|
||||
if (value == null)
|
||||
return String.Empty;
|
||||
|
||||
return value.Replace("\0", "\\0");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RuleSet Name="FxCop rules for xUnit.net" Description="Rules for running FxCop against xUnit.net framework assemblies" ToolsVersion="11.0">
|
||||
<IncludeAll Action="Error" />
|
||||
<Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
|
||||
<Rule Id="CA1002" Action="None" />
|
||||
<Rule Id="CA1063" Action="None" />
|
||||
<Rule Id="CA1303" Action="None" />
|
||||
<Rule Id="CA1720" Action="None" />
|
||||
<Rule Id="CA1813" Action="None" />
|
||||
<Rule Id="CA1816" Action="None" />
|
||||
</Rules>
|
||||
</RuleSet>
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
using Android.App;
|
||||
using Android.OS;
|
||||
|
||||
using MonoDroid.Dialog;
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
|
||||
[Activity (Label = "Credits")]
|
||||
internal class CreditsActivity : DialogActivity {
|
||||
|
||||
const string notice = "<br><b>xUnit Android Runner</b><br>Copyright © 2014<br>Outercurve Foundation<br>All rights reserved.<br><br>Author: Oren Novotny<br>";
|
||||
|
||||
protected override void OnCreate (Bundle bundle)
|
||||
{
|
||||
Root = new RootElement (String.Empty) {
|
||||
new FormattedSection (notice) {
|
||||
new HtmlElement ("About Xamarin", "http://xamarin.com"),
|
||||
new HtmlElement ("About Mono for Android", "http://android.xamarin.com"),
|
||||
new HtmlElement ("About MonoDroid.Dialog", "https://github.com/spouliot/MonoDroid.Dialog"),
|
||||
new HtmlElement("About xUnit", "https://github.com/xunit/xunit"),
|
||||
}
|
||||
};
|
||||
|
||||
base.OnCreate (bundle);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Android.Views;
|
||||
|
||||
using MonoDroid.Dialog;
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
|
||||
[Activity (Label = "Options", WindowSoftInputMode = SoftInput.AdjustPan,
|
||||
ConfigurationChanges = ConfigChanges.KeyboardHidden | ConfigChanges.Orientation)]
|
||||
internal class OptionsActivity : DialogActivity {
|
||||
BooleanElement remote;
|
||||
EntryElement host_name;
|
||||
EntryElement host_port;
|
||||
|
||||
protected override void OnCreate (Bundle bundle)
|
||||
{
|
||||
RunnerOptions options = AndroidRunner.Runner.Options;
|
||||
|
||||
var nameDisplayGroup = new RadioGroup("nameDisplay", options.NameDisplay == NameDisplay.Short ? 1 : 0); ;
|
||||
var nameDisplayFull = new RadioElement("Full", "nameDisplay");
|
||||
var nameDisplayShort = new RadioElement("Short", "nameDisplay");
|
||||
|
||||
remote = new BooleanElement ("Remote Server", options.EnableNetwork);
|
||||
host_name = new EntryElement ("HostName", options.HostName);
|
||||
host_port = new EntryElement ("Port", options.HostPort.ToString ()) { Numeric = true };
|
||||
|
||||
var par = new BooleanElement("Parallelize Assemblies", options.ParallelizeAssemblies);
|
||||
|
||||
|
||||
Root = new RootElement("Options")
|
||||
{
|
||||
new Section()
|
||||
{
|
||||
remote,
|
||||
host_name,
|
||||
host_port
|
||||
},
|
||||
|
||||
new Section("Execution") { par },
|
||||
|
||||
new Section("Display")
|
||||
{
|
||||
new[]
|
||||
{
|
||||
new RootElement("Name Display", nameDisplayGroup)
|
||||
{
|
||||
new Section()
|
||||
{
|
||||
nameDisplayFull,
|
||||
nameDisplayShort
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
base.OnCreate (bundle);
|
||||
}
|
||||
|
||||
int GetPort ()
|
||||
{
|
||||
int port;
|
||||
ushort p;
|
||||
if (UInt16.TryParse (host_port.Value, out p))
|
||||
port = p;
|
||||
else
|
||||
port = -1;
|
||||
return port;
|
||||
}
|
||||
|
||||
protected override void OnPause ()
|
||||
{
|
||||
var options = AndroidRunner.Runner.Options;
|
||||
options.EnableNetwork = remote.Value;
|
||||
options.HostName = host_name.Value;
|
||||
options.HostPort = GetPort ();
|
||||
options.Save (this);
|
||||
base.OnPause ();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.OS;
|
||||
using Android.Widget;
|
||||
using MonoDroid.Dialog;
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
public class RunnerActivity : Activity
|
||||
{
|
||||
private Assembly assembly;
|
||||
|
||||
public RunnerActivity()
|
||||
{
|
||||
//Initialized = (AndroidRunner.AssemblyLevel.Count > 0);
|
||||
}
|
||||
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
public AndroidRunner Runner
|
||||
{
|
||||
get { return AndroidRunner.Runner; }
|
||||
}
|
||||
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
base.OnCreate(bundle);
|
||||
|
||||
var view = Runner.GetView(this);
|
||||
|
||||
Initialized = true;
|
||||
|
||||
SetContentView(view);
|
||||
}
|
||||
|
||||
public void Add(Assembly assembly)
|
||||
{
|
||||
if (assembly == null)
|
||||
throw new ArgumentNullException("assembly");
|
||||
|
||||
// this can be called many times but we only want to load them
|
||||
// once since we need to share them across most activities
|
||||
if (!Initialized)
|
||||
{
|
||||
AndroidRunner.AddAssembly(assembly);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddExecutionAssembly(Assembly assembly)
|
||||
{
|
||||
if (assembly == null) throw new ArgumentNullException("assembly");
|
||||
this.assembly = assembly;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.OS;
|
||||
using Android.Widget;
|
||||
using MonoDroid.Dialog;
|
||||
using Xunit.Abstractions;
|
||||
using Environment = System.Environment;
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
[Activity(Label = "Results")]
|
||||
public class TestResultActivity : Activity
|
||||
{
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
base.OnCreate(bundle);
|
||||
|
||||
var testCaseUniqueId = Intent.GetStringExtra("TestCase");
|
||||
|
||||
var result = AndroidRunner.Runner.Results[testCaseUniqueId];
|
||||
|
||||
var message = string.Empty;
|
||||
if (result.TestCase.Result == TestState.Failed)
|
||||
{
|
||||
message = String.Format("<b>{0}<b><br><font color='grey'>{1}</font>",
|
||||
result.ErrorMessage, result.ErrorStackTrace.Replace(Environment.NewLine, "<br>"));
|
||||
}
|
||||
else if (result.TestCase.Result == TestState.Skipped)
|
||||
{
|
||||
message = String.Format("<b>{0}<b>",
|
||||
((ITestSkipped) result.TestResultMessage).Reason);
|
||||
}
|
||||
|
||||
|
||||
var menu = new RootElement(String.Empty)
|
||||
{
|
||||
new Section(result.TestCase.DisplayName)
|
||||
{
|
||||
new FormattedElement(message)
|
||||
}
|
||||
};
|
||||
|
||||
var da = new DialogAdapter(this, menu);
|
||||
var lv = new ListView(this)
|
||||
{
|
||||
Adapter = da
|
||||
};
|
||||
SetContentView(lv);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.OS;
|
||||
using Android.Widget;
|
||||
using MonoDroid.Dialog;
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
[Activity(Label = "Tests")]
|
||||
public class TestSuiteActivity : Activity
|
||||
{
|
||||
private Section main;
|
||||
private string sourceName;
|
||||
private TestSuiteElement suiteElement;
|
||||
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
base.OnCreate(bundle);
|
||||
|
||||
sourceName = Intent.GetStringExtra("TestSuite");
|
||||
suiteElement = AndroidRunner.Runner.Suites[sourceName];
|
||||
|
||||
var menu = new RootElement(String.Empty);
|
||||
|
||||
main = new Section(sourceName);
|
||||
foreach (var test in suiteElement.TestCases)
|
||||
{
|
||||
main.Add(test);
|
||||
}
|
||||
menu.Add(main);
|
||||
|
||||
var options = new Section()
|
||||
{
|
||||
new ActionElement("Run all", Run),
|
||||
};
|
||||
menu.Add(options);
|
||||
|
||||
var da = new DialogAdapter(this, menu);
|
||||
var lv = new ListView(this)
|
||||
{
|
||||
Adapter = da
|
||||
};
|
||||
SetContentView(lv);
|
||||
}
|
||||
|
||||
private async void Run()
|
||||
{
|
||||
var runner = AndroidRunner.Runner;
|
||||
|
||||
await runner.Run(suiteElement.TestCases.Select(tc => tc.TestCase));
|
||||
|
||||
suiteElement.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,535 @@
|
|||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.OS;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using MonoDroid.Dialog;
|
||||
using Xunit.Runners.UI;
|
||||
using Xunit.Runners.Utilities;
|
||||
using Xunit.Runners.Visitors;
|
||||
using Debug = System.Diagnostics.Debug;
|
||||
|
||||
|
||||
namespace Xunit.Runners
|
||||
{
|
||||
public class AndroidRunner : ITestListener
|
||||
{
|
||||
private static readonly AndroidRunner runner = new AndroidRunner();
|
||||
private static readonly List<Assembly> assemblies = new List<Assembly>();
|
||||
|
||||
private readonly AsyncLock executionLock = new AsyncLock();
|
||||
private readonly ManualResetEvent mre = new ManualResetEvent(false);
|
||||
private readonly Dictionary<string, MonoTestResult> results = new Dictionary<string, MonoTestResult>();
|
||||
private readonly Dictionary<string, TestSuiteElement> suiteElements = new Dictionary<string, TestSuiteElement>();
|
||||
private bool cancelled;
|
||||
private int failed;
|
||||
private RunnerOptions options;
|
||||
private int passed;
|
||||
private int skipped;
|
||||
private Dictionary<string, IEnumerable<MonoTestCase>> testCasesByAssembly = new Dictionary<string, IEnumerable<MonoTestCase>>();
|
||||
|
||||
private AndroidRunner()
|
||||
{
|
||||
}
|
||||
|
||||
public bool AutoStart { get; set; }
|
||||
|
||||
public bool TerminateAfterExecution { get; set; }
|
||||
|
||||
public RunnerOptions Options
|
||||
{
|
||||
get
|
||||
{
|
||||
if (options == null)
|
||||
options = new RunnerOptions();
|
||||
return options;
|
||||
}
|
||||
set { options = value; }
|
||||
}
|
||||
|
||||
|
||||
public static AndroidRunner Runner
|
||||
{
|
||||
get { return runner; }
|
||||
}
|
||||
|
||||
public IDictionary<string, MonoTestResult> Results
|
||||
{
|
||||
get { return results; }
|
||||
}
|
||||
|
||||
internal IDictionary<string, TestSuiteElement> Suites
|
||||
{
|
||||
get { return suiteElements; }
|
||||
}
|
||||
|
||||
public TextWriter Writer { get; set; }
|
||||
|
||||
private bool OpenWriter(string message)
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
// let the application provide it's own TextWriter to ease automation with AutoStart property
|
||||
if (Writer == null)
|
||||
{
|
||||
if (Options.ShowUseNetworkLogger)
|
||||
{
|
||||
Console.WriteLine("[{0}] Sending '{1}' results to {2}:{3}", now, message, Options.HostName, Options.HostPort);
|
||||
try
|
||||
{
|
||||
Writer = new TcpTextWriter(Options.HostName, Options.HostPort);
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
var msg = String.Format("Cannot connect to {0}:{1}. Start network service or disable network option", options.HostName, options.HostPort);
|
||||
Toast.MakeText(Application.Context, msg, ToastLength.Long)
|
||||
.Show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer = Console.Out;
|
||||
}
|
||||
}
|
||||
|
||||
Writer.WriteLine("[Runner executing:\t{0}]", message);
|
||||
// FIXME
|
||||
Writer.WriteLine("[M4A Version:\t{0}]", "???");
|
||||
|
||||
Writer.WriteLine("[Board:\t\t{0}]", Build.Board);
|
||||
Writer.WriteLine("[Bootloader:\t{0}]", Build.Bootloader);
|
||||
Writer.WriteLine("[Brand:\t\t{0}]", Build.Brand);
|
||||
Writer.WriteLine("[CpuAbi:\t{0} {1}]", Build.CpuAbi, Build.CpuAbi2);
|
||||
Writer.WriteLine("[Device:\t{0}]", Build.Device);
|
||||
Writer.WriteLine("[Display:\t{0}]", Build.Display);
|
||||
Writer.WriteLine("[Fingerprint:\t{0}]", Build.Fingerprint);
|
||||
Writer.WriteLine("[Hardware:\t{0}]", Build.Hardware);
|
||||
Writer.WriteLine("[Host:\t\t{0}]", Build.Host);
|
||||
Writer.WriteLine("[Id:\t\t{0}]", Build.Id);
|
||||
Writer.WriteLine("[Manufacturer:\t{0}]", Build.Manufacturer);
|
||||
Writer.WriteLine("[Model:\t\t{0}]", Build.Model);
|
||||
Writer.WriteLine("[Product:\t{0}]", Build.Product);
|
||||
Writer.WriteLine("[Radio:\t\t{0}]", Build.Radio);
|
||||
Writer.WriteLine("[Tags:\t\t{0}]", Build.Tags);
|
||||
Writer.WriteLine("[Time:\t\t{0}]", Build.Time);
|
||||
Writer.WriteLine("[Type:\t\t{0}]", Build.Type);
|
||||
Writer.WriteLine("[User:\t\t{0}]", Build.User);
|
||||
Writer.WriteLine("[VERSION.Codename:\t{0}]", Build.VERSION.Codename);
|
||||
Writer.WriteLine("[VERSION.Incremental:\t{0}]", Build.VERSION.Incremental);
|
||||
Writer.WriteLine("[VERSION.Release:\t{0}]", Build.VERSION.Release);
|
||||
Writer.WriteLine("[VERSION.Sdk:\t\t{0}]", Build.VERSION.Sdk);
|
||||
Writer.WriteLine("[VERSION.SdkInt:\t{0}]", Build.VERSION.SdkInt);
|
||||
Writer.WriteLine("[Device Date/Time:\t{0}]", now); // to match earlier C.WL output
|
||||
|
||||
// FIXME: add data about how the app was compiled (e.g. ARMvX, LLVM, Linker options)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CloseWriter()
|
||||
{
|
||||
Writer.Close();
|
||||
Writer = null;
|
||||
}
|
||||
|
||||
void ITestListener.RecordResult(MonoTestResult result)
|
||||
{
|
||||
Application.SynchronizationContext.Post(_ =>
|
||||
{
|
||||
Results[result.TestCase.UniqueName] = result;
|
||||
|
||||
result.RaiseTestUpdated();
|
||||
}, null);
|
||||
|
||||
if (result.TestCase.Result == TestState.Passed)
|
||||
{
|
||||
Writer.Write("\t[PASS] ");
|
||||
passed++;
|
||||
}
|
||||
else if (result.TestCase.Result == TestState.Skipped)
|
||||
{
|
||||
Writer.Write("\t[SKIPPED] ");
|
||||
skipped++;
|
||||
}
|
||||
else if (result.TestCase.Result == TestState.Failed)
|
||||
{
|
||||
Writer.Write("\t[FAIL] ");
|
||||
failed++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write("\t[INFO] ");
|
||||
}
|
||||
Writer.Write(result.TestCase.DisplayName);
|
||||
|
||||
var message = result.ErrorMessage;
|
||||
if (!String.IsNullOrEmpty(message))
|
||||
{
|
||||
Writer.Write(" : {0}", message.Replace("\r\n", "\\r\\n"));
|
||||
}
|
||||
Writer.WriteLine();
|
||||
|
||||
var stacktrace = result.ErrorStackTrace;
|
||||
if (!String.IsNullOrEmpty(result.ErrorStackTrace))
|
||||
{
|
||||
var lines = stacktrace.Split(new char[] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var line in lines)
|
||||
Writer.WriteLine("\t\t{0}", line);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<IGrouping<string, MonoTestCase>> DiscoverTestsInAssemblies()
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
var result = new List<IGrouping<string, MonoTestCase>>();
|
||||
|
||||
try
|
||||
{
|
||||
using (AssemblyHelper.SubscribeResolve())
|
||||
{
|
||||
foreach (var assm in assemblies)
|
||||
{
|
||||
// Xunit needs the file name
|
||||
var fileName = Path.GetFileName(assm.Location);
|
||||
|
||||
try
|
||||
{
|
||||
using (var framework = new XunitFrontController(fileName, configFileName: null, shadowCopy: true))
|
||||
using (var sink = new TestDiscoveryVisitor())
|
||||
{
|
||||
framework.Find(includeSourceInformation: true, messageSink: sink, options: new TestFrameworkOptions());
|
||||
sink.Finished.WaitOne();
|
||||
|
||||
result.Add(
|
||||
new Grouping<string, MonoTestCase>(
|
||||
fileName,
|
||||
sink.TestCases
|
||||
.GroupBy(tc => String.Format("{0}.{1}", tc.Class.Name, tc.Method.Name))
|
||||
.SelectMany(group =>
|
||||
group.Select(testCase =>
|
||||
new MonoTestCase(fileName, testCase, forceUniqueNames: group.Count() > 1)))
|
||||
.ToList()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e);
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal View GetView(Activity activity)
|
||||
{
|
||||
if (Options == null)
|
||||
Options = new RunnerOptions(activity);
|
||||
|
||||
RunnerOptions.Initialize(activity);
|
||||
|
||||
Results.Clear();
|
||||
suiteElements.Clear();
|
||||
|
||||
var menu = new RootElement("Test Runner");
|
||||
|
||||
var main = new Section("Loading test assemblies...");
|
||||
|
||||
var optSect = new Section()
|
||||
{
|
||||
new ActivityElement("Options", typeof(OptionsActivity)),
|
||||
new ActivityElement("Credits", typeof(CreditsActivity))
|
||||
};
|
||||
|
||||
menu.Add(main);
|
||||
menu.Add(optSect);
|
||||
|
||||
var a = new DialogAdapter(activity, menu);
|
||||
var lv = new ListView(activity)
|
||||
{
|
||||
Adapter = a
|
||||
};
|
||||
|
||||
|
||||
ThreadPool.QueueUserWorkItem(_ =>
|
||||
{
|
||||
var allTests = DiscoverTestsInAssemblies();
|
||||
testCasesByAssembly = allTests.ToDictionary(cases => cases.Key, cases => cases as IEnumerable<MonoTestCase>);
|
||||
|
||||
|
||||
activity.RunOnUiThread(() =>
|
||||
{
|
||||
foreach (var kvp in testCasesByAssembly)
|
||||
{
|
||||
main.Add(SetupSource(kvp.Key, kvp.Value));
|
||||
}
|
||||
|
||||
|
||||
mre.Set();
|
||||
main.Caption = null;
|
||||
|
||||
optSect.Insert(0, new ActionElement("Run Everything", async () => await Run()));
|
||||
|
||||
a.NotifyDataSetChanged();
|
||||
});
|
||||
|
||||
assemblies.Clear();
|
||||
});
|
||||
|
||||
|
||||
// AutoStart running the tests (with either the supplied 'writer' or the options)
|
||||
if (AutoStart)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(delegate
|
||||
{
|
||||
mre.WaitOne();
|
||||
activity.RunOnUiThread(async () =>
|
||||
{
|
||||
await Run();
|
||||
|
||||
// optionally end the process,
|
||||
if (TerminateAfterExecution)
|
||||
activity.Finish();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return lv;
|
||||
}
|
||||
|
||||
internal static void AddAssembly(Assembly testAssm)
|
||||
{
|
||||
assemblies.Add(testAssm);
|
||||
}
|
||||
|
||||
internal Task Run()
|
||||
{
|
||||
return Run(testCasesByAssembly.Values.SelectMany(v => v), "Run Everything");
|
||||
}
|
||||
|
||||
internal Task Run(MonoTestCase test)
|
||||
{
|
||||
return Run(new[] {test});
|
||||
}
|
||||
|
||||
internal async Task Run(IEnumerable<MonoTestCase> tests, string message = null)
|
||||
{
|
||||
var stopWatch = Stopwatch.StartNew();
|
||||
|
||||
var groups = tests.GroupBy(t => t.AssemblyFileName);
|
||||
|
||||
using (await executionLock.LockAsync())
|
||||
{
|
||||
if (message == null)
|
||||
message = tests.Count() > 1 ? "Run Multiple Tests" : tests.First()
|
||||
.DisplayName;
|
||||
if (!OpenWriter(message))
|
||||
return;
|
||||
try
|
||||
{
|
||||
await RunTests(groups, stopWatch);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseWriter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TestSuiteElement SetupSource(string sourceName, IEnumerable<MonoTestCase> testSource)
|
||||
{
|
||||
var root = new RootElement("Tests");
|
||||
|
||||
var elements = new List<TestCaseElement>();
|
||||
|
||||
var section = new Section(sourceName);
|
||||
foreach (var test in testSource)
|
||||
{
|
||||
var ele = new TestCaseElement(test, this);
|
||||
elements.Add(ele);
|
||||
section.Add(ele);
|
||||
}
|
||||
|
||||
var tse = new TestSuiteElement(sourceName, elements, this);
|
||||
suiteElements[sourceName] = tse;
|
||||
|
||||
|
||||
root.Add(section);
|
||||
|
||||
if (section.Count > 1)
|
||||
{
|
||||
StringElement allbtn = null;
|
||||
allbtn = new StringElement("Run all",
|
||||
async delegate
|
||||
{
|
||||
await Run(testSource);
|
||||
});
|
||||
var options = new Section()
|
||||
{
|
||||
allbtn
|
||||
};
|
||||
|
||||
root.Add(options);
|
||||
}
|
||||
return tse;
|
||||
}
|
||||
|
||||
|
||||
private Task RunTests(IEnumerable<IGrouping<string, MonoTestCase>> testCaseAccessor, Stopwatch stopwatch)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>(null);
|
||||
|
||||
ThreadPool.QueueUserWorkItem(state =>
|
||||
{
|
||||
var toDispose = new List<IDisposable>();
|
||||
|
||||
try
|
||||
{
|
||||
cancelled = false;
|
||||
|
||||
using (AssemblyHelper.SubscribeResolve())
|
||||
if (RunnerOptions.Current.ParallelizeAssemblies)
|
||||
testCaseAccessor
|
||||
.Select(testCaseGroup => RunTestsInAssemblyAsync(toDispose, testCaseGroup.Key, testCaseGroup, stopwatch))
|
||||
.ToList()
|
||||
.ForEach(@event => @event.WaitOne());
|
||||
else
|
||||
testCaseAccessor
|
||||
.ToList()
|
||||
.ForEach(testCaseGroup => RunTestsInAssembly(toDispose, testCaseGroup.Key, testCaseGroup, stopwatch));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
tcs.SetException(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
toDispose.ForEach(disposable => disposable.Dispose());
|
||||
OnTestRunCompleted();
|
||||
tcs.SetResult(null);
|
||||
}
|
||||
});
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
private ManualResetEvent RunTestsInAssemblyAsync(List<IDisposable> toDispose,
|
||||
string assemblyFileName,
|
||||
IEnumerable<MonoTestCase> testCases,
|
||||
Stopwatch stopwatch)
|
||||
{
|
||||
var @event = new ManualResetEvent(initialState: false);
|
||||
|
||||
ThreadPool.QueueUserWorkItem(_ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
RunTestsInAssembly(toDispose, assemblyFileName, testCases, stopwatch);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@event.Set();
|
||||
}
|
||||
});
|
||||
|
||||
return @event;
|
||||
}
|
||||
|
||||
private void RunTestsInAssembly(List<IDisposable> toDispose,
|
||||
string assemblyFileName,
|
||||
IEnumerable<MonoTestCase> testCases,
|
||||
Stopwatch stopwatch)
|
||||
{
|
||||
if (cancelled)
|
||||
return;
|
||||
|
||||
var controller = new XunitFrontController(assemblyFileName, configFileName: null, shadowCopy: true);
|
||||
|
||||
lock (toDispose)
|
||||
toDispose.Add(controller);
|
||||
|
||||
var xunitTestCases = testCases.ToDictionary(tc => tc.TestCase);
|
||||
|
||||
using (var executionVisitor = new MonoTestExecutionVisitor(xunitTestCases, this, () => cancelled))
|
||||
{
|
||||
var executionOptions = new XunitExecutionOptions
|
||||
{
|
||||
//DisableParallelization = !settings.ParallelizeTestCollections,
|
||||
//MaxParallelThreads = settings.MaxParallelThreads
|
||||
};
|
||||
|
||||
controller.RunTests(xunitTestCases.Keys.ToList(), executionVisitor, executionOptions);
|
||||
executionVisitor.Finished.WaitOne();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTestRunCompleted()
|
||||
{
|
||||
Application.SynchronizationContext.Post(_ =>
|
||||
{
|
||||
foreach (var ts in suiteElements.Values)
|
||||
{
|
||||
// Recalc the status
|
||||
ts.Refresh();
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
private class Grouping<TKey, TElement> : IGrouping<TKey, TElement>
|
||||
{
|
||||
private readonly IEnumerable<TElement> elements;
|
||||
|
||||
public Grouping(TKey key, IEnumerable<TElement> elements)
|
||||
{
|
||||
Key = key;
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
public TKey Key { get; private set; }
|
||||
|
||||
public IEnumerator<TElement> GetEnumerator()
|
||||
{
|
||||
return elements.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return elements.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
using Android.Content;
|
||||
using Android.Views;
|
||||
|
||||
using MonoDroid.Dialog;
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
|
||||
internal class ActionElement : StringElement {
|
||||
|
||||
Action action;
|
||||
|
||||
public ActionElement (string name, Action action) : base (name)
|
||||
{
|
||||
this.action = action;
|
||||
Value = "..."; // hint some action will take place
|
||||
}
|
||||
|
||||
public override View GetView (Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
View view = base.GetView (context, convertView, parent);
|
||||
view.Click += delegate {
|
||||
// FIXME: show activity/progress
|
||||
action ();
|
||||
};
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
using Android.Content;
|
||||
using Android.Views;
|
||||
|
||||
using MonoDroid.Dialog;
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
|
||||
internal class ActivityElement : StringElement
|
||||
{
|
||||
|
||||
Type activity;
|
||||
|
||||
public ActivityElement (string name, Type type) : base (name)
|
||||
{
|
||||
activity = type;
|
||||
Value = ">"; // hint there's something more to show
|
||||
}
|
||||
|
||||
public override View GetView (Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
View view = base.GetView (context, convertView, parent);
|
||||
view.Click += delegate {
|
||||
Intent intent = new Intent (context, activity);
|
||||
intent.AddFlags (ActivityFlags.NewTask);
|
||||
context.StartActivity (intent);
|
||||
};
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
using Android.Content;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
using MonoDroid.Dialog;
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
|
||||
class FormattedElement : StringElement {
|
||||
|
||||
private new TextView _caption;
|
||||
private new TextView _text;
|
||||
|
||||
private string captionText;
|
||||
|
||||
|
||||
public FormattedElement (string caption) : base (caption)
|
||||
{
|
||||
}
|
||||
|
||||
public string Indicator {
|
||||
get; set;
|
||||
}
|
||||
|
||||
public override View GetView (Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
var view = new RelativeLayout(context);
|
||||
|
||||
var parms = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent,
|
||||
ViewGroup.LayoutParams.WrapContent);
|
||||
parms.SetMargins(5, 3, 5, 0);
|
||||
parms.AddRule(LayoutRules.AlignParentLeft);
|
||||
|
||||
_caption = new TextView (context);
|
||||
if (string.IsNullOrWhiteSpace(captionText))
|
||||
SetCaption(Caption);
|
||||
else
|
||||
{
|
||||
SetCaption(captionText);
|
||||
captionText = null;
|
||||
}
|
||||
view.AddView(_caption, parms);
|
||||
|
||||
if (!String.IsNullOrWhiteSpace (Indicator)) {
|
||||
var tparms = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent,
|
||||
ViewGroup.LayoutParams.WrapContent);
|
||||
tparms.SetMargins(5, 3, 5, 5);
|
||||
tparms.AddRule(LayoutRules.CenterVertical);
|
||||
tparms.AddRule(LayoutRules.AlignParentRight);
|
||||
|
||||
_text = new TextView (context) {
|
||||
Text = Indicator,
|
||||
TextSize = 22f
|
||||
};
|
||||
view.AddView(_text, tparms);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
public void SetCaption(string html)
|
||||
{
|
||||
if (_caption != null)
|
||||
_caption.SetText(Android.Text.Html.FromHtml(html), TextView.BufferType.Spannable);
|
||||
|
||||
captionText = html;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
using MonoDroid.Dialog;
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
|
||||
// can't really name it HtmlSection wrt HtmlElement ;-)
|
||||
internal class FormattedSection : Section {
|
||||
|
||||
public FormattedSection (string html)
|
||||
: base (html)
|
||||
{
|
||||
}
|
||||
|
||||
public override View GetView (Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
TextView tv = new TextView (context);
|
||||
tv.TextSize = 20f;
|
||||
tv.SetText (Android.Text.Html.FromHtml (Caption), TextView.BufferType.Spannable);
|
||||
|
||||
var parms = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
|
||||
parms.AddRule (LayoutRules.CenterHorizontal);
|
||||
|
||||
RelativeLayout view = new RelativeLayout (context, null, Android.Resource.Attribute.ListSeparatorTextViewStyle);
|
||||
view.AddView (tv, parms);
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Android.Content;
|
||||
using Android.Views;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
internal class TestCaseElement : TestElement
|
||||
{
|
||||
public TestCaseElement(MonoTestCase testCase, AndroidRunner runner)
|
||||
: base(runner)
|
||||
{
|
||||
if (testCase == null) throw new ArgumentNullException("testCase");
|
||||
|
||||
TestCase = testCase;
|
||||
|
||||
MonoTestResult result;
|
||||
Runner.Results.TryGetValue(testCase.UniqueName, out result);
|
||||
|
||||
if (testCase.Result == TestState.NotRun)
|
||||
Indicator = "..."; // hint there's more
|
||||
|
||||
Refresh();
|
||||
|
||||
testCase.TestCaseUpdated += OnTestCaseUpdated;
|
||||
}
|
||||
|
||||
private void OnTestCaseUpdated(object sender, EventArgs e)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
||||
public MonoTestCase TestCase { get; private set; }
|
||||
|
||||
|
||||
|
||||
public override TestState Result
|
||||
{
|
||||
get { return TestCase.Result; }
|
||||
}
|
||||
|
||||
protected override string GetCaption()
|
||||
{
|
||||
|
||||
|
||||
|
||||
if (TestCase.Result == TestState.Skipped)
|
||||
{
|
||||
var val = ((ITestSkipped)TestCase.TestResult.TestResultMessage).Reason;
|
||||
return string.Format("<b>{0}</b><br><font color='#FF7700'>{1}: {2}</font>",
|
||||
TestCase.DisplayName, TestState.Skipped, val);
|
||||
}
|
||||
else if (TestCase.Result == TestState.Passed)
|
||||
{
|
||||
Indicator = null;
|
||||
return string.Format("<b>{0}</b><br><font color='green'>Success! {1} ms</font>", TestCase.DisplayName, TestCase.TestResult.Duration.TotalMilliseconds);
|
||||
}
|
||||
else if (TestCase.Result == TestState.Failed)
|
||||
{
|
||||
var val = TestCase.TestResult.ErrorMessage;
|
||||
return string.Format("<b>{0}</b><br><font color='red'>{1}</font>", TestCase.DisplayName, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Assert.Ignore falls into this
|
||||
var val = TestCase.TestResult.ErrorMessage;
|
||||
return string.Format("<b>{0}</b><br><font color='grey'>{1}</font>", TestCase.DisplayName, val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task Run()
|
||||
{
|
||||
if (TestCase.Result == TestState.NotRun)
|
||||
{
|
||||
await Runner.Run(TestCase);
|
||||
}
|
||||
}
|
||||
|
||||
public override View GetView(Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
Refresh();
|
||||
var view = base.GetView(context, convertView, parent);
|
||||
view.Click += async delegate
|
||||
{
|
||||
await Run();
|
||||
|
||||
if (Result != TestState.Passed)
|
||||
{
|
||||
var intent = new Intent(context, typeof(TestResultActivity));
|
||||
intent.PutExtra("TestCase", TestCase.UniqueName);
|
||||
intent.AddFlags(ActivityFlags.NewTask);
|
||||
context.StartActivity(intent);
|
||||
}
|
||||
};
|
||||
return view;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
TestCase.TestCaseUpdated -= OnTestCaseUpdated;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
using Android.Graphics;
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
|
||||
internal abstract class TestElement : FormattedElement
|
||||
{
|
||||
|
||||
protected TestElement(AndroidRunner runner)
|
||||
: base(String.Empty)
|
||||
{
|
||||
if (runner == null) throw new ArgumentNullException("runner");
|
||||
|
||||
Runner = runner;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected virtual void OptionsChanged()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public abstract TestState Result { get; }
|
||||
|
||||
protected abstract string GetCaption();
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
var caption = GetCaption();
|
||||
SetCaption(caption);
|
||||
}
|
||||
|
||||
protected AndroidRunner Runner { get; private set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Android.Content;
|
||||
using Android.Views;
|
||||
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
internal class TestSuiteElement : TestElement
|
||||
{
|
||||
private readonly string sourceName;
|
||||
private readonly IEnumerable<TestCaseElement> testCases = Enumerable.Empty<TestCaseElement>();
|
||||
private TestState result = TestState.NotRun;
|
||||
|
||||
public IEnumerable<TestCaseElement> TestCases { get { return testCases; } }
|
||||
|
||||
public TestSuiteElement(string sourceName, IEnumerable<TestCaseElement> testCases, AndroidRunner runner)
|
||||
: base(runner)
|
||||
{
|
||||
this.sourceName = sourceName;
|
||||
this.testCases = testCases;
|
||||
|
||||
if (testCases.Any())
|
||||
Indicator = ">"; // hint there's more
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
||||
protected override string GetCaption()
|
||||
{
|
||||
var count = testCases.Count();
|
||||
var caption = String.Format("<b>{0}</b><br>", sourceName);
|
||||
if (count == 0)
|
||||
{
|
||||
caption += "<font color='#ff7f00'>no test was found inside this suite</font>";
|
||||
}
|
||||
else
|
||||
{
|
||||
var outcomes = testCases.GroupBy(r => r.Result);
|
||||
|
||||
var results = outcomes.ToDictionary(k => k.Key, v => v.Count());
|
||||
|
||||
int positive;
|
||||
results.TryGetValue(TestState.Passed, out positive);
|
||||
|
||||
int failure;
|
||||
results.TryGetValue(TestState.Failed, out failure);
|
||||
|
||||
int skipped;
|
||||
results.TryGetValue(TestState.Skipped, out skipped);
|
||||
|
||||
int notRun;
|
||||
results.TryGetValue(TestState.NotRun, out notRun);
|
||||
|
||||
// No failures and all run
|
||||
if (failure == 0 && notRun == 0)
|
||||
{
|
||||
caption += string.Format("<font color='green'><b>Success!</b> {0} test{1}</font>",
|
||||
positive, positive == 1 ? string.Empty : "s");
|
||||
|
||||
result = TestState.Passed;
|
||||
}
|
||||
else if (failure > 0 || (notRun > 0 && notRun < count))
|
||||
{
|
||||
// we either have failures or some of the tests are not run
|
||||
caption += String.Format("<font color='green'>{0} success,</font> <font color='red'>{1} failure{2}, {3} skip{4}, {5} not run</font>",
|
||||
positive, failure, failure > 1 ? "s" : String.Empty,
|
||||
skipped, skipped > 1 ? "s" : String.Empty,
|
||||
notRun);
|
||||
|
||||
result = TestState.Failed;
|
||||
}
|
||||
else if (Result == TestState.NotRun)
|
||||
{
|
||||
caption += String.Format("<font color='green'><b>{0}</b> test case{1}, <i>{2}</i></font>",
|
||||
count, count == 1 ? String.Empty : "s", Result);
|
||||
}
|
||||
}
|
||||
return caption;
|
||||
}
|
||||
|
||||
public override View GetView(Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
Refresh();
|
||||
var view = base.GetView(context, convertView, parent);
|
||||
// if there are test cases inside this suite then create an activity to show them
|
||||
if (testCases.Any())
|
||||
{
|
||||
view.Click += delegate
|
||||
{
|
||||
var intent = new Intent(context, typeof(TestSuiteActivity));
|
||||
intent.PutExtra("TestSuite", sourceName);
|
||||
intent.AddFlags(ActivityFlags.NewTask);
|
||||
context.StartActivity(intent);
|
||||
};
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
public override TestState Result
|
||||
{
|
||||
get { return result; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,401 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Android.Content;
|
||||
using Android.Widget;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class BindingContext : IDisposable
|
||||
{
|
||||
public RootElement Root;
|
||||
Dictionary<Element, MemberAndInstance> mappings;
|
||||
private Context _context;
|
||||
|
||||
class MemberAndInstance
|
||||
{
|
||||
public MemberAndInstance(MemberInfo mi, object o)
|
||||
{
|
||||
Member = mi;
|
||||
Obj = o;
|
||||
}
|
||||
public MemberInfo Member;
|
||||
public object Obj;
|
||||
}
|
||||
|
||||
static object GetValue(MemberInfo mi, object o)
|
||||
{
|
||||
var fi = mi as FieldInfo;
|
||||
if (fi != null)
|
||||
return fi.GetValue(o);
|
||||
var pi = mi as PropertyInfo;
|
||||
|
||||
var getMethod = pi.GetGetMethod();
|
||||
return getMethod.Invoke(o, new object[0]);
|
||||
}
|
||||
|
||||
static void SetValue(MemberInfo mi, object o, object val)
|
||||
{
|
||||
var fi = mi as FieldInfo;
|
||||
if (fi != null)
|
||||
{
|
||||
fi.SetValue(o, val);
|
||||
return;
|
||||
}
|
||||
var pi = mi as PropertyInfo;
|
||||
var setMethod = pi.GetSetMethod();
|
||||
setMethod.Invoke(o, new object[] { val });
|
||||
}
|
||||
|
||||
static string MakeCaption(string name)
|
||||
{
|
||||
var sb = new StringBuilder(name.Length);
|
||||
bool nextUp = true;
|
||||
|
||||
foreach (char c in name)
|
||||
{
|
||||
if (nextUp)
|
||||
{
|
||||
sb.Append(Char.ToUpper(c));
|
||||
nextUp = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == '_')
|
||||
{
|
||||
sb.Append(' ');
|
||||
continue;
|
||||
}
|
||||
if (Char.IsUpper(c))
|
||||
sb.Append(' ');
|
||||
sb.Append(c);
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// Returns the type for fields and properties and null for everything else
|
||||
static Type GetTypeForMember(MemberInfo mi)
|
||||
{
|
||||
if (mi is FieldInfo)
|
||||
return ((FieldInfo)mi).FieldType;
|
||||
else if (mi is PropertyInfo)
|
||||
return ((PropertyInfo)mi).PropertyType;
|
||||
return null;
|
||||
}
|
||||
|
||||
public BindingContext(Context context, object callbacks, object o, string title)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
if (o == null)
|
||||
throw new ArgumentNullException("o");
|
||||
|
||||
mappings = new Dictionary<Element, MemberAndInstance>();
|
||||
|
||||
Root = new RootElement(title);
|
||||
Populate(callbacks, o, Root);
|
||||
}
|
||||
|
||||
void Populate(object callbacks, object o, RootElement root)
|
||||
{
|
||||
MemberInfo last_radio_index = null;
|
||||
var members = o.GetType().GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public |
|
||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
Section section = null;
|
||||
|
||||
foreach (var mi in members)
|
||||
{
|
||||
Type mType = GetTypeForMember(mi);
|
||||
|
||||
if (mType == null)
|
||||
continue;
|
||||
|
||||
string caption = null;
|
||||
object[] attrs = mi.GetCustomAttributes(false);
|
||||
bool skip = false;
|
||||
foreach (var attr in attrs)
|
||||
{
|
||||
if (attr is SkipAttribute)
|
||||
skip = true;
|
||||
if (attr is CaptionAttribute)
|
||||
caption = ((CaptionAttribute)attr).Caption;
|
||||
else if (attr is SectionAttribute)
|
||||
{
|
||||
if (section != null)
|
||||
root.Add(section);
|
||||
var sa = attr as SectionAttribute;
|
||||
section = new Section(sa.Caption, sa.Footer);
|
||||
}
|
||||
}
|
||||
if (skip)
|
||||
continue;
|
||||
|
||||
if (caption == null)
|
||||
caption = MakeCaption(mi.Name);
|
||||
|
||||
if (section == null)
|
||||
section = new Section();
|
||||
|
||||
Element element = null;
|
||||
if (mType == typeof(string))
|
||||
{
|
||||
PasswordAttribute pa = null;
|
||||
AlignmentAttribute align = null;
|
||||
EntryAttribute ea = null;
|
||||
object html = null;
|
||||
EventHandler invoke = null;
|
||||
bool multi = false;
|
||||
|
||||
foreach (object attr in attrs)
|
||||
{
|
||||
if (attr is PasswordAttribute)
|
||||
pa = attr as PasswordAttribute;
|
||||
else if (attr is EntryAttribute)
|
||||
ea = attr as EntryAttribute;
|
||||
else if (attr is MultilineAttribute)
|
||||
multi = true;
|
||||
else if (attr is HtmlAttribute)
|
||||
html = attr;
|
||||
else if (attr is AlignmentAttribute)
|
||||
align = attr as AlignmentAttribute;
|
||||
|
||||
if (attr is OnTapAttribute)
|
||||
{
|
||||
string mname = ((OnTapAttribute)attr).Method;
|
||||
|
||||
if (callbacks == null)
|
||||
{
|
||||
throw new Exception("Your class contains [OnTap] attributes, but you passed a null object for `context' in the constructor");
|
||||
}
|
||||
|
||||
var method = callbacks.GetType().GetMethod(mname);
|
||||
if (method == null)
|
||||
throw new Exception("Did not find method " + mname);
|
||||
invoke = delegate
|
||||
{
|
||||
method.Invoke(method.IsStatic ? null : callbacks, new object[0]);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
string value = (string)GetValue(mi, o);
|
||||
if (pa != null)
|
||||
element = new EntryElement(caption, value) { Hint = pa.Placeholder, Password = true };
|
||||
else if (ea != null)
|
||||
element = new EntryElement(caption, value) { Hint = ea.Placeholder };
|
||||
else if (multi)
|
||||
element = new MultilineElement(caption, value);
|
||||
else if (html != null)
|
||||
element = new HtmlElement(caption, value);
|
||||
else
|
||||
{
|
||||
var selement = new StringElement(caption, value);
|
||||
element = selement;
|
||||
|
||||
if (align != null)
|
||||
selement.Alignment = align.Alignment;
|
||||
}
|
||||
|
||||
if (invoke != null)
|
||||
((StringElement)element).Click = invoke;
|
||||
}
|
||||
else if (mType == typeof(float))
|
||||
{
|
||||
var floatElement = new FloatElement(null, null, (int)GetValue(mi, o));
|
||||
floatElement.Caption = caption;
|
||||
element = floatElement;
|
||||
|
||||
foreach (object attr in attrs)
|
||||
{
|
||||
if (attr is RangeAttribute)
|
||||
{
|
||||
var ra = attr as RangeAttribute;
|
||||
floatElement.MinValue = ra.Low;
|
||||
floatElement.MaxValue = ra.High;
|
||||
floatElement.ShowCaption = ra.ShowCaption;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mType == typeof(bool))
|
||||
{
|
||||
bool checkbox = false;
|
||||
foreach (object attr in attrs)
|
||||
{
|
||||
if (attr is CheckboxAttribute)
|
||||
checkbox = true;
|
||||
}
|
||||
|
||||
if (checkbox)
|
||||
element = new CheckboxElement(caption, (bool)GetValue(mi, o));
|
||||
else
|
||||
element = new BooleanElement(caption, (bool)GetValue(mi, o));
|
||||
}
|
||||
else if (mType == typeof(DateTime))
|
||||
{
|
||||
var dateTime = (DateTime)GetValue(mi, o);
|
||||
bool asDate = false, asTime = false;
|
||||
|
||||
foreach (object attr in attrs)
|
||||
{
|
||||
if (attr is DateAttribute)
|
||||
asDate = true;
|
||||
else if (attr is TimeAttribute)
|
||||
asTime = true;
|
||||
}
|
||||
|
||||
if (asDate)
|
||||
element = new DateElement(caption, dateTime);
|
||||
else if (asTime)
|
||||
element = new TimeElement(caption, dateTime);
|
||||
else
|
||||
element = new DateTimeElement(caption, dateTime);
|
||||
}
|
||||
else if (mType.IsEnum)
|
||||
{
|
||||
var csection = new Section();
|
||||
ulong evalue = Convert.ToUInt64(GetValue(mi, o), null);
|
||||
int idx = 0;
|
||||
int selected = 0;
|
||||
|
||||
foreach (var fi in mType.GetFields(BindingFlags.Public | BindingFlags.Static))
|
||||
{
|
||||
ulong v = Convert.ToUInt64(GetValue(fi, null));
|
||||
|
||||
if (v == evalue)
|
||||
selected = idx;
|
||||
|
||||
csection.Add(new RadioElement(MakeCaption(fi.Name)));
|
||||
idx++;
|
||||
}
|
||||
|
||||
element = new RootElement(caption, new RadioGroup(null, selected)) { csection };
|
||||
}
|
||||
else if (mType == typeof(ImageView))
|
||||
{
|
||||
element = new ImageElement(null); // (ImageView)GetValue(mi, o));
|
||||
}
|
||||
else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(mType))
|
||||
{
|
||||
var csection = new Section();
|
||||
int count = 0;
|
||||
|
||||
if (last_radio_index == null)
|
||||
throw new Exception("IEnumerable found, but no previous int found");
|
||||
foreach (var e in (IEnumerable)GetValue(mi, o))
|
||||
{
|
||||
csection.Add(new RadioElement(e.ToString()));
|
||||
count++;
|
||||
}
|
||||
int selected = (int)GetValue(last_radio_index, o);
|
||||
if (selected >= count || selected < 0)
|
||||
selected = 0;
|
||||
element = new RootElement(caption, new MemberRadioGroup(null, selected, last_radio_index)) { csection };
|
||||
last_radio_index = null;
|
||||
}
|
||||
else if (typeof(int) == mType)
|
||||
{
|
||||
foreach (object attr in attrs)
|
||||
{
|
||||
if (attr is RadioSelectionAttribute)
|
||||
{
|
||||
last_radio_index = mi;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var nested = GetValue(mi, o);
|
||||
if (nested != null)
|
||||
{
|
||||
var newRoot = new RootElement(caption);
|
||||
Populate(callbacks, nested, newRoot);
|
||||
element = newRoot;
|
||||
}
|
||||
}
|
||||
|
||||
if (element == null)
|
||||
continue;
|
||||
|
||||
section.Add(element);
|
||||
mappings[element] = new MemberAndInstance(mi, o);
|
||||
}
|
||||
root.Add(section);
|
||||
}
|
||||
|
||||
class MemberRadioGroup : RadioGroup
|
||||
{
|
||||
public MemberInfo mi;
|
||||
|
||||
public MemberRadioGroup(string key, int selected, MemberInfo mi)
|
||||
: base(key, selected)
|
||||
{
|
||||
this.mi = mi;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
foreach (var element in mappings.Keys)
|
||||
{
|
||||
element.Dispose();
|
||||
}
|
||||
mappings = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Fetch()
|
||||
{
|
||||
foreach (var dk in mappings)
|
||||
{
|
||||
Element element = dk.Key;
|
||||
MemberInfo mi = dk.Value.Member;
|
||||
object obj = dk.Value.Obj;
|
||||
|
||||
if (element is DateTimeElement)
|
||||
SetValue(mi, obj, ((DateTimeElement)element).DateValue);
|
||||
else if (element is FloatElement)
|
||||
SetValue(mi, obj, ((FloatElement)element).Value);
|
||||
else if (element is BooleanElement)
|
||||
SetValue(mi, obj, ((BooleanElement)element).Value);
|
||||
else if (element is CheckboxElement)
|
||||
SetValue(mi, obj, ((CheckboxElement)element).Value);
|
||||
else if (element is EntryElement)
|
||||
{
|
||||
var entry = (EntryElement)element;
|
||||
// TODO: entry.FetchValue();
|
||||
SetValue(mi, obj, entry.Value);
|
||||
}
|
||||
else if (element is ImageElement)
|
||||
SetValue(mi, obj, ((ImageElement)element).Value);
|
||||
else if (element is RootElement)
|
||||
{
|
||||
var re = element as RootElement;
|
||||
if (re._group as MemberRadioGroup != null)
|
||||
{
|
||||
var group = re._group as MemberRadioGroup;
|
||||
SetValue(group.mi, obj, re.RadioSelected);
|
||||
}
|
||||
else if (re._group as RadioGroup != null)
|
||||
{
|
||||
var mType = GetTypeForMember(mi);
|
||||
var fi = mType.GetFields(BindingFlags.Public | BindingFlags.Static)[re.RadioSelected];
|
||||
|
||||
SetValue(mi, obj, fi.GetValue(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using Android.Content;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal abstract class BoolElement : Element
|
||||
{
|
||||
private bool _val;
|
||||
|
||||
public bool Value
|
||||
{
|
||||
get { return _val; }
|
||||
set
|
||||
{
|
||||
if (_val != value)
|
||||
{
|
||||
_val = value;
|
||||
if (ValueChanged != null)
|
||||
ValueChanged(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler ValueChanged;
|
||||
|
||||
public BoolElement(string caption, bool value) : base(caption)
|
||||
{
|
||||
_val = value;
|
||||
}
|
||||
|
||||
public BoolElement(string caption, bool value, int layoutId)
|
||||
: base(caption, layoutId)
|
||||
{
|
||||
_val = value;
|
||||
}
|
||||
|
||||
public override string Summary()
|
||||
{
|
||||
return _val ? "On" : "Off";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to display toggle button on the screen.
|
||||
/// </summary>
|
||||
internal class BooleanElement : BoolElement, CompoundButton.IOnCheckedChangeListener
|
||||
{
|
||||
private ToggleButton _toggleButton;
|
||||
private TextView _caption;
|
||||
private TextView _subCaption;
|
||||
|
||||
public BooleanElement(string caption, bool value)
|
||||
: base(caption, value, (int) DroidResources.ElementLayout.dialog_onofffieldright)
|
||||
{
|
||||
}
|
||||
|
||||
public BooleanElement(string caption, bool value, int layoutId)
|
||||
: base(caption, value, layoutId)
|
||||
{
|
||||
}
|
||||
|
||||
public override View GetView(Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
View toggleButtonView;
|
||||
View view = DroidResources.LoadBooleanElementLayout(context, convertView, parent, LayoutId, out _caption, out _subCaption, out toggleButtonView);
|
||||
|
||||
if (view != null)
|
||||
{
|
||||
_caption.Text = Caption;
|
||||
_toggleButton = toggleButtonView as ToggleButton;
|
||||
_toggleButton.SetOnCheckedChangeListener(null);
|
||||
_toggleButton.Checked = Value;
|
||||
_toggleButton.SetOnCheckedChangeListener(this);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
//_toggleButton.Dispose();
|
||||
_toggleButton = null;
|
||||
//_caption.Dispose();
|
||||
_caption = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCheckedChanged(CompoundButton buttonView, bool isChecked)
|
||||
{
|
||||
this.Value = isChecked;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
|
||||
using Android.Content;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class ButtonElement : StringElement
|
||||
{
|
||||
public ButtonElement (string caption, EventHandler tapped)
|
||||
: base(caption, (int)DroidResources.ElementLayout.dialog_button)
|
||||
{
|
||||
this.Click = tapped;
|
||||
}
|
||||
|
||||
public override View GetView (Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
Button button;
|
||||
var view = DroidResources.LoadButtonLayout (context, convertView, parent, LayoutId, out button);
|
||||
if (view != null) {
|
||||
button.Text = Caption;
|
||||
if (Click != null)
|
||||
button.Click += Click;
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public override string Summary ()
|
||||
{
|
||||
return Caption;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
using System;
|
||||
using Android.Content;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class CheckboxElement : Element, CompoundButton.IOnCheckedChangeListener
|
||||
{
|
||||
public bool Value
|
||||
{
|
||||
get { return _val; }
|
||||
set
|
||||
{
|
||||
bool emit = _val != value;
|
||||
_val = value;
|
||||
if(_checkbox != null && _checkbox.Checked != _val)
|
||||
_checkbox.Checked = _val;
|
||||
else if (emit && ValueChanged != null)
|
||||
ValueChanged(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
private bool _val;
|
||||
|
||||
public string SubCaption
|
||||
{
|
||||
get
|
||||
{
|
||||
return subCap;
|
||||
}
|
||||
set
|
||||
{
|
||||
subCap = value;
|
||||
}
|
||||
}
|
||||
private string subCap;
|
||||
|
||||
public bool ReadOnly
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public event EventHandler ValueChanged;
|
||||
|
||||
private CheckBox _checkbox;
|
||||
private TextView _caption;
|
||||
private TextView _subCaption;
|
||||
|
||||
public string Group;
|
||||
|
||||
public CheckboxElement(string caption)
|
||||
: base(caption, (int)DroidResources.ElementLayout.dialog_boolfieldright)
|
||||
{
|
||||
Value = false;
|
||||
}
|
||||
|
||||
public CheckboxElement(string caption, bool value)
|
||||
: base(caption, (int)DroidResources.ElementLayout.dialog_boolfieldright)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public CheckboxElement(string caption, bool value, string subCaption, string group)
|
||||
: base(caption, (int)DroidResources.ElementLayout.dialog_boolfieldsubright)
|
||||
{
|
||||
Value = value;
|
||||
Group = group;
|
||||
SubCaption = subCaption;
|
||||
}
|
||||
|
||||
public CheckboxElement(string caption, bool value, string group)
|
||||
: base(caption, (int)DroidResources.ElementLayout.dialog_boolfieldright)
|
||||
{
|
||||
Value = value;
|
||||
Group = group;
|
||||
}
|
||||
|
||||
public CheckboxElement(string caption, bool value, string group, int layoutId)
|
||||
: base(caption, layoutId)
|
||||
{
|
||||
Value = value;
|
||||
Group = group;
|
||||
}
|
||||
|
||||
public override View GetView(Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
View checkboxView;
|
||||
View view = DroidResources.LoadBooleanElementLayout(context, convertView, parent, LayoutId, out _caption, out _subCaption, out checkboxView);
|
||||
if (view != null)
|
||||
{
|
||||
_caption.Text = Caption;
|
||||
|
||||
_checkbox = checkboxView as CheckBox;
|
||||
_checkbox.SetOnCheckedChangeListener(null);
|
||||
_checkbox.Checked = Value;
|
||||
_checkbox.Clickable = !ReadOnly;
|
||||
|
||||
if (_subCaption != null)
|
||||
_subCaption.Text = SubCaption;
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
public void OnCheckedChanged(CompoundButton buttonView, bool isChecked)
|
||||
{
|
||||
this.Value = isChecked;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
using System;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class DateTimeElement : StringElement
|
||||
{
|
||||
public DateTime DateValue
|
||||
{
|
||||
get { return DateTime.Parse(Value); }
|
||||
set { Value = Format(value); }
|
||||
}
|
||||
|
||||
public DateTimeElement(string caption, DateTime date)
|
||||
: base(caption)
|
||||
{
|
||||
DateValue = date;
|
||||
}
|
||||
|
||||
public DateTimeElement(string caption, DateTime date, int layoutId)
|
||||
: base(caption, layoutId)
|
||||
{
|
||||
DateValue = date;
|
||||
}
|
||||
|
||||
public virtual string Format(DateTime dt)
|
||||
{
|
||||
return dt.ToShortDateString() + " " + dt.ToShortTimeString();
|
||||
}
|
||||
}
|
||||
|
||||
internal class DateElement : DateTimeElement
|
||||
{
|
||||
public DateElement(string caption, DateTime date)
|
||||
: base(caption, date)
|
||||
{
|
||||
this.Click = delegate { EditDate(); };
|
||||
}
|
||||
|
||||
public DateElement(string caption, DateTime date, int layoutId)
|
||||
: base(caption, date, layoutId)
|
||||
{
|
||||
this.Click = delegate { EditDate(); };
|
||||
}
|
||||
|
||||
public override string Format(DateTime dt)
|
||||
{
|
||||
return dt.ToShortDateString();
|
||||
}
|
||||
|
||||
// the event received when the user "sets" the date in the dialog
|
||||
void OnDateSet(object sender, DatePickerDialog.DateSetEventArgs e)
|
||||
{
|
||||
DateTime current = DateValue;
|
||||
DateValue = new DateTime(e.Date.Year, e.Date.Month, e.Date.Day, current.Hour, current.Minute, 0);
|
||||
}
|
||||
|
||||
private void EditDate()
|
||||
{
|
||||
Context context = GetContext();
|
||||
if (context == null)
|
||||
{
|
||||
Android.Util.Log.Warn("DateElement", "No Context for Edit");
|
||||
return;
|
||||
}
|
||||
DateTime val = DateValue;
|
||||
new DatePickerDialog(context, OnDateSet, val.Year, val.Month - 1, val.Day).Show();
|
||||
}
|
||||
}
|
||||
|
||||
internal class TimeElement : DateTimeElement
|
||||
{
|
||||
public TimeElement(string caption, DateTime date)
|
||||
: base(caption, date)
|
||||
{
|
||||
this.Click = delegate { EditDate(); };
|
||||
}
|
||||
|
||||
public TimeElement(string caption, DateTime date, int layoutId)
|
||||
: base(caption, date, layoutId)
|
||||
{
|
||||
this.Click = delegate { EditDate(); };
|
||||
}
|
||||
|
||||
public override string Format(DateTime dt)
|
||||
{
|
||||
return dt.ToShortTimeString();
|
||||
}
|
||||
|
||||
// the event received when the user "sets" the date in the dialog
|
||||
void OnDateSet(object sender, TimePickerDialog.TimeSetEventArgs e)
|
||||
{
|
||||
DateTime current = DateValue;
|
||||
DateValue = new DateTime(current.Year, current.Month, current.Day, e.HourOfDay, e.Minute, 0);
|
||||
}
|
||||
|
||||
private void EditDate()
|
||||
{
|
||||
Context context = GetContext();
|
||||
if (context == null)
|
||||
{
|
||||
Android.Util.Log.Warn("TimeElement", "No Context for Edit");
|
||||
return;
|
||||
}
|
||||
DateTime val = DateValue;
|
||||
// TODO: get the current time setting for thge 24 hour clock
|
||||
new TimePickerDialog(context, OnDateSet, val.Hour, val.Minute, false).Show();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Android.App;
|
||||
using Android.OS;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class DialogInstanceData : Java.Lang.Object
|
||||
{
|
||||
public DialogInstanceData()
|
||||
{
|
||||
_dialogState = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
private Dictionary<String, String> _dialogState;
|
||||
}
|
||||
|
||||
internal class DialogActivity : ListActivity
|
||||
{
|
||||
public RootElement Root { get; set; }
|
||||
private DialogHelper Dialog { get; set; }
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
Dialog = new DialogHelper(this, this.ListView, this.Root);
|
||||
|
||||
if (this.LastNonConfigurationInstance != null)
|
||||
{
|
||||
// apply value changes that are saved
|
||||
}
|
||||
}
|
||||
|
||||
public override Java.Lang.Object OnRetainNonConfigurationInstance()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
using Android.Content;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class DialogAdapter : BaseAdapter<Section>
|
||||
{
|
||||
const int TYPE_SECTION_HEADER = 0;
|
||||
|
||||
Context context;
|
||||
LayoutInflater inflater;
|
||||
|
||||
public DialogAdapter(Context context, RootElement root)
|
||||
{
|
||||
this.context = context;
|
||||
this.inflater = LayoutInflater.From(context);
|
||||
this.Root = root;
|
||||
}
|
||||
|
||||
public RootElement Root { get; set; }
|
||||
|
||||
public override bool IsEnabled(int position)
|
||||
{
|
||||
// start counting from here
|
||||
int typeOffset = TYPE_SECTION_HEADER + 1;
|
||||
|
||||
foreach (var s in Root.Sections)
|
||||
{
|
||||
if (position == 0)
|
||||
return false;
|
||||
|
||||
int size = s.Adapter.Count + 1;
|
||||
|
||||
if (position < size)
|
||||
return true;
|
||||
|
||||
position -= size;
|
||||
typeOffset += s.Adapter.ViewTypeCount;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
//Get each adapter's count + 1 for the header
|
||||
foreach (var s in Root.Sections)
|
||||
count += s.Adapter.Count + 1;
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
public override int ViewTypeCount
|
||||
{
|
||||
get
|
||||
{
|
||||
// ViewTypeCount is the same as Count for these,
|
||||
// there are as many ViewTypes as Views as everyone is unique!
|
||||
return Count;
|
||||
}
|
||||
}
|
||||
|
||||
public Element ElementAtIndex(int position)
|
||||
{
|
||||
int sectionIndex = 0;
|
||||
foreach (var s in Root.Sections)
|
||||
{
|
||||
if (position == 0)
|
||||
return this.Root.Sections[sectionIndex];
|
||||
|
||||
// note: plus one for the section header view
|
||||
int size = s.Adapter.Count + 1;
|
||||
if (position < size)
|
||||
return this.Root.Sections[sectionIndex].Elements[position - 1];
|
||||
|
||||
position -= size;
|
||||
sectionIndex++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Section this[int position]
|
||||
{
|
||||
get { return this.Root.Sections[position]; }
|
||||
}
|
||||
|
||||
public override bool AreAllItemsEnabled()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetItemViewType(int position)
|
||||
{
|
||||
// start counting from here
|
||||
int typeOffset = TYPE_SECTION_HEADER + 1;
|
||||
|
||||
foreach (var s in Root.Sections)
|
||||
{
|
||||
if (position == 0)
|
||||
return (TYPE_SECTION_HEADER);
|
||||
|
||||
int size = s.Adapter.Count + 1;
|
||||
|
||||
if (position < size)
|
||||
return (typeOffset + s.Adapter.GetItemViewType(position - 1));
|
||||
|
||||
position -= size;
|
||||
typeOffset += s.Adapter.ViewTypeCount;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public override long GetItemId(int position)
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
public override View GetView(int position, View convertView, ViewGroup parent)
|
||||
{
|
||||
int sectionIndex = 0;
|
||||
|
||||
foreach (var s in Root.Sections)
|
||||
{
|
||||
if (s.Adapter.Context == null)
|
||||
s.Adapter.Context = this.context;
|
||||
|
||||
if (position == 0)
|
||||
return s.GetView(context, convertView, parent);
|
||||
|
||||
int size = s.Adapter.Count + 1;
|
||||
|
||||
if (position < size)
|
||||
return (s.Adapter.GetView(position - 1, convertView, parent));
|
||||
|
||||
position -= size;
|
||||
sectionIndex++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using Android.Content;
|
||||
using Android.Widget;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class DialogHelper
|
||||
{
|
||||
private Context context;
|
||||
private RootElement formLayer;
|
||||
|
||||
//public event Action<Section, Element> ElementClick;
|
||||
//public event Action<Section, Element> ElementLongClick;
|
||||
|
||||
public RootElement Root { get; set; }
|
||||
|
||||
private DialogAdapter DialogAdapter { get; set; }
|
||||
|
||||
public DialogHelper(Context context, ListView dialogView, RootElement root)
|
||||
{
|
||||
this.Root = root;
|
||||
this.Root.Context = context;
|
||||
|
||||
dialogView.Adapter = this.DialogAdapter = new DialogAdapter(context, this.Root);
|
||||
dialogView.ItemClick += new EventHandler<AdapterView.ItemClickEventArgs>(ListView_ItemClick);
|
||||
//dialogView.ItemLongClick += new EventHandler<ItemEventArgs>(ListView_ItemLongClick);
|
||||
dialogView.Tag = root;
|
||||
}
|
||||
|
||||
void ListView_ItemLongClick(object sender, ItemEventArgs e)
|
||||
{
|
||||
var elem = this.DialogAdapter.ElementAtIndex(e.Position);
|
||||
if (elem != null && elem.LongClick != null)
|
||||
elem.LongClick(sender, e);
|
||||
}
|
||||
|
||||
void ListView_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
|
||||
{
|
||||
var elem = this.DialogAdapter.ElementAtIndex(e.Position);
|
||||
if (elem != null && elem.Click != null)
|
||||
elem.Click(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Android.Content;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal static class DroidResources
|
||||
{
|
||||
public enum ElementLayout: int
|
||||
{
|
||||
dialog_boolfieldleft,
|
||||
dialog_boolfieldright,
|
||||
dialog_boolfieldsubleft,
|
||||
dialog_boolfieldsubright,
|
||||
|
||||
dialog_button,
|
||||
dialog_datefield,
|
||||
dialog_fieldsetlabel,
|
||||
dialog_labelfieldbelow,
|
||||
dialog_labelfieldright,
|
||||
dialog_onofffieldright,
|
||||
dialog_panel,
|
||||
dialog_root,
|
||||
dialog_selectlist,
|
||||
dialog_selectlistfield,
|
||||
dialog_textarea,
|
||||
|
||||
dialog_floatimage,
|
||||
|
||||
dialog_textfieldbelow,
|
||||
dialog_textfieldright,
|
||||
}
|
||||
|
||||
public static View LoadFloatElementLayout(Context context, View convertView, ViewGroup parent, int layoutId, out TextView label, out SeekBar slider, out ImageView left, out ImageView right)
|
||||
{
|
||||
View layout = convertView ?? LoadLayout(context, parent, layoutId);
|
||||
if (layout != null)
|
||||
{
|
||||
label = layout.FindViewById<TextView>(context.Resources.GetIdentifier("dialog_LabelField", "id", context.PackageName));
|
||||
slider = layout.FindViewById<SeekBar>(context.Resources.GetIdentifier("dialog_SliderField", "id", context.PackageName));
|
||||
left = layout.FindViewById<ImageView>(context.Resources.GetIdentifier("dialog_ImageLeft", "id", context.PackageName));
|
||||
right = layout.FindViewById<ImageView>(context.Resources.GetIdentifier("dialog_ImageRight", "id", context.PackageName));
|
||||
}
|
||||
else
|
||||
{
|
||||
label = null;
|
||||
slider = null;
|
||||
left = right = null;
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
||||
private static View LoadLayout(Context context, ViewGroup parent, int layoutId)
|
||||
{
|
||||
try
|
||||
{
|
||||
LayoutInflater inflater = LayoutInflater.FromContext(context);
|
||||
if (_resourceMap.ContainsKey((ElementLayout)layoutId))
|
||||
{
|
||||
string layoutName = _resourceMap[(ElementLayout)layoutId];
|
||||
int layoutIndex = context.Resources.GetIdentifier(layoutName, "layout", context.PackageName);
|
||||
return inflater.Inflate(layoutIndex, parent, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: figure out what context to use to get this right, currently doesn't inflate application resources
|
||||
return inflater.Inflate(layoutId, parent, false);
|
||||
}
|
||||
}
|
||||
catch (InflateException ex)
|
||||
{
|
||||
Log.Error("MDD", "Inflate failed: " + ex.Cause.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("MDD", "LoadLayout failed: " + ex.Message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static View LoadStringElementLayout(Context context, View convertView, ViewGroup parent, int layoutId, out TextView label, out TextView value)
|
||||
{
|
||||
// We are not recycling views here because Android.Dialog is leaking anon.Click handlers
|
||||
//View layout = convertView ?? LoadLayout(context, parent, layoutId);
|
||||
View layout = LoadLayout(context, parent, layoutId);
|
||||
if (layout != null)
|
||||
{
|
||||
label = layout.FindViewById<TextView>(context.Resources.GetIdentifier("dialog_LabelField", "id", context.PackageName));
|
||||
value = layout.FindViewById<TextView>(context.Resources.GetIdentifier("dialog_ValueField", "id", context.PackageName));
|
||||
}
|
||||
else
|
||||
{
|
||||
label = null;
|
||||
value = null;
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
public static View LoadButtonLayout(Context context, View convertView, ViewGroup parent, int layoutId, out Button button)
|
||||
{
|
||||
View layout = LoadLayout(context, parent, layoutId);
|
||||
if (layout != null)
|
||||
{
|
||||
button = layout.FindViewById<Button>(context.Resources.GetIdentifier("dialog_Button", "id", context.PackageName));
|
||||
}
|
||||
else
|
||||
{
|
||||
button = null;
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
public static View LoadMultilineElementLayout(Context context, View convertView, ViewGroup parent, int layoutId, out EditText value)
|
||||
{
|
||||
View layout = convertView ?? LoadLayout(context, parent, layoutId);
|
||||
if (layout != null)
|
||||
{
|
||||
value = layout.FindViewById<EditText>(context.Resources.GetIdentifier("dialog_ValueField", "id", context.PackageName));
|
||||
}
|
||||
else
|
||||
{
|
||||
value = null;
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
public static View LoadBooleanElementLayout(Context context, View convertView, ViewGroup parent, int layoutId, out TextView label, out TextView subLabel, out View value)
|
||||
{
|
||||
View layout = convertView ?? LoadLayout(context, parent, layoutId);
|
||||
if (layout != null)
|
||||
{
|
||||
label = layout.FindViewById<TextView>(context.Resources.GetIdentifier("dialog_LabelField", "id", context.PackageName));
|
||||
value = layout.FindViewById<View>(context.Resources.GetIdentifier("dialog_BoolField", "id", context.PackageName));
|
||||
int id = context.Resources.GetIdentifier("dialog_LabelSubtextField", "id", context.PackageName);
|
||||
subLabel = (id >= 0) ? layout.FindViewById<TextView>(id): null;
|
||||
}
|
||||
else
|
||||
{
|
||||
label = null;
|
||||
value = null;
|
||||
subLabel = null;
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
public static View LoadStringEntryLayout(Context context, View convertView, ViewGroup parent, int layoutId, out TextView label, out EditText value)
|
||||
{
|
||||
View layout = LoadLayout(context, parent, layoutId);
|
||||
if (layout != null)
|
||||
{
|
||||
label = layout.FindViewById<TextView>(context.Resources.GetIdentifier("dialog_LabelField", "id", context.PackageName));
|
||||
value = layout.FindViewById<EditText>(context.Resources.GetIdentifier("dialog_ValueField", "id", context.PackageName));
|
||||
}
|
||||
else
|
||||
{
|
||||
label = null;
|
||||
value = null;
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
private static Dictionary<ElementLayout, string> _resourceMap;
|
||||
|
||||
static DroidResources()
|
||||
{
|
||||
_resourceMap = new Dictionary<ElementLayout, string>()
|
||||
{
|
||||
// Label templates
|
||||
{ ElementLayout.dialog_labelfieldbelow, "dialog_labelfieldbelow"},
|
||||
{ ElementLayout.dialog_labelfieldright, "dialog_labelfieldright"},
|
||||
|
||||
// Boolean and Checkbox templates
|
||||
{ ElementLayout.dialog_boolfieldleft, "dialog_boolfieldleft"},
|
||||
{ ElementLayout.dialog_boolfieldright, "dialog_boolfieldright"},
|
||||
{ ElementLayout.dialog_boolfieldsubleft, "dialog_boolfieldsubleft"},
|
||||
{ ElementLayout.dialog_boolfieldsubright, "dialog_boolfieldsubright"},
|
||||
{ ElementLayout.dialog_onofffieldright, "dialog_onofffieldright"},
|
||||
|
||||
// Root templates
|
||||
{ ElementLayout.dialog_root, "dialog_root"},
|
||||
|
||||
// Entry templates
|
||||
{ ElementLayout.dialog_textfieldbelow, "dialog_textfieldbelow"},
|
||||
{ ElementLayout.dialog_textfieldright, "dialog_textfieldright"},
|
||||
|
||||
// Slider
|
||||
{ ElementLayout.dialog_floatimage, "dialog_floatimage"},
|
||||
|
||||
// Button templates
|
||||
{ ElementLayout.dialog_button, "dialog_button"},
|
||||
|
||||
// Date
|
||||
{ ElementLayout.dialog_datefield, "dialog_datefield"},
|
||||
|
||||
//
|
||||
{ ElementLayout.dialog_fieldsetlabel, "dialog_fieldsetlabel"},
|
||||
|
||||
{ ElementLayout.dialog_panel, "dialog_panel"},
|
||||
|
||||
//
|
||||
{ ElementLayout.dialog_selectlist, "dialog_selectlist"},
|
||||
{ ElementLayout.dialog_selectlistfield, "dialog_selectlistfield"},
|
||||
{ ElementLayout.dialog_textarea, "dialog_textarea"},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
using Android.Content;
|
||||
using Android.Views;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal abstract class Element : Java.Lang.Object, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the element with the given caption.
|
||||
/// </summary>
|
||||
/// <param name="caption">
|
||||
/// The caption.
|
||||
/// </param>
|
||||
public Element(string caption)
|
||||
{
|
||||
Caption = caption;
|
||||
}
|
||||
|
||||
public Element(string caption, int layoutId)
|
||||
{
|
||||
Caption = caption;
|
||||
LayoutId = layoutId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The caption to display for this given element
|
||||
/// </summary>
|
||||
public string Caption { get; set; }
|
||||
|
||||
public int LayoutId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Handle to the container object.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For sections this points to a RootElement, for every other object this points to a Section and it is null
|
||||
/// for the root RootElement.
|
||||
/// </remarks>
|
||||
public Element Parent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Override for click the click event
|
||||
/// </summary>
|
||||
public EventHandler Click { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Override for long click events, some elements use this for action
|
||||
/// </summary>
|
||||
public EventHandler LongClick { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An Object that contains data about the element. The default is null.
|
||||
/// </summary>
|
||||
public Object Tag { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing) { }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a summary of the value represented by this object, suitable
|
||||
/// for rendering as the result of a RootElement with child objects.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The return value must be a short description of the value.
|
||||
/// </returns>
|
||||
public virtual string Summary()
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overriden my most derived classes, creates a view that creates a View with the contents for display
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="convertView"></param>
|
||||
/// <param name="parent"></param>
|
||||
/// <returns></returns>
|
||||
public virtual View GetView(Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
return LayoutId == 0 ? new View(context) : null;
|
||||
}
|
||||
|
||||
public virtual void Selected() { }
|
||||
|
||||
public virtual bool Matches(string text)
|
||||
{
|
||||
return Caption != null && Caption.IndexOf(text, StringComparison.CurrentCultureIgnoreCase) != -1;
|
||||
}
|
||||
|
||||
public Context GetContext()
|
||||
{
|
||||
Element element = this;
|
||||
while (element.Parent != null)
|
||||
element = element.Parent;
|
||||
|
||||
RootElement rootElement = element as RootElement;
|
||||
return rootElement == null ? null : rootElement.Context;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
using Android.Content;
|
||||
using Android.Text;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class EntryElement : Element, ITextWatcher
|
||||
{
|
||||
public string Value
|
||||
{
|
||||
get { return _val; }
|
||||
set
|
||||
{
|
||||
_val = value;
|
||||
if (_entry != null && _entry.Text != value)
|
||||
{
|
||||
_entry.Text = value;
|
||||
if (ValueChanged != null)
|
||||
ValueChanged(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler ValueChanged;
|
||||
|
||||
public EntryElement(string caption, string value)
|
||||
: this(caption, value, (int)DroidResources.ElementLayout.dialog_textfieldright)
|
||||
{
|
||||
}
|
||||
|
||||
public EntryElement(string caption, string value, int layoutId)
|
||||
: base(caption, layoutId)
|
||||
{
|
||||
_val = value;
|
||||
Lines = 1;
|
||||
}
|
||||
|
||||
public override string Summary()
|
||||
{
|
||||
return _val;
|
||||
}
|
||||
|
||||
public bool Password { get; set; }
|
||||
public bool Numeric { get; set; }
|
||||
public string Hint { get; set; }
|
||||
public int Lines { get; set; }
|
||||
|
||||
protected EditText _entry;
|
||||
private string _val;
|
||||
protected Action _entryClicked{get;set;}
|
||||
|
||||
public override View GetView(Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
Log.Debug("MDD", "EntryElement: GetView: ConvertView: " + ((convertView == null) ? "false" : "true") +
|
||||
" Value: " + Value + " Hint: " + Hint + " Password: " + (Password ? "true" : "false"));
|
||||
|
||||
TextView label;
|
||||
var view = DroidResources.LoadStringEntryLayout(context, convertView, parent, LayoutId, out label, out _entry);
|
||||
if (view != null)
|
||||
{
|
||||
// Warning! Crazy ass hack ahead!
|
||||
// since we can't know when out convertedView was was swapped from inside us, we store the
|
||||
// old textwatcher in the tag element so it can be removed!!!! (barf, rech, yucky!)
|
||||
if (_entry.Tag != null)
|
||||
_entry.RemoveTextChangedListener((ITextWatcher)_entry.Tag);
|
||||
|
||||
_entry.Text = this.Value;
|
||||
_entry.Hint = this.Hint;
|
||||
|
||||
if (this.Password)
|
||||
_entry.InputType = (InputTypes.ClassText | InputTypes.TextVariationPassword);
|
||||
else if (this.Numeric)
|
||||
_entry.InputType = (InputTypes.ClassNumber | InputTypes.NumberFlagDecimal | InputTypes.NumberFlagSigned);
|
||||
else
|
||||
_entry.InputType = InputTypes.ClassText;
|
||||
|
||||
// continuation of crazy ass hack, stash away the listener value so we can look it up later
|
||||
_entry.Tag = this;
|
||||
_entry.AddTextChangedListener(this);
|
||||
|
||||
label.Text = (label != null) ? Caption: string.Empty;
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
//_entry.Dispose();
|
||||
_entry = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Matches(string text)
|
||||
{
|
||||
return (Value != null ? Value.IndexOf(text, StringComparison.CurrentCultureIgnoreCase) != -1 : false) || base.Matches(text);
|
||||
}
|
||||
|
||||
public void OnTextChanged(Java.Lang.ICharSequence s, int start, int before, int count)
|
||||
{
|
||||
this.Value = s.ToString();
|
||||
}
|
||||
|
||||
public void AfterTextChanged(IEditable s)
|
||||
{
|
||||
// nothing needed
|
||||
}
|
||||
|
||||
public void BeforeTextChanged(Java.Lang.ICharSequence s, int start, int count, int after)
|
||||
{
|
||||
// nothing needed
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
using System;
|
||||
using Android.Content;
|
||||
using Android.Graphics;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class FloatElement : Element, SeekBar.IOnSeekBarChangeListener
|
||||
{
|
||||
public bool ShowCaption;
|
||||
public int Value;
|
||||
public int MinValue, MaxValue;
|
||||
public Bitmap Left;
|
||||
public Bitmap Right;
|
||||
|
||||
public FloatElement(string caption)
|
||||
: this(caption, (int)DroidResources.ElementLayout.dialog_floatimage)
|
||||
{
|
||||
Value = 0;
|
||||
MinValue = 0;
|
||||
MaxValue = 10;
|
||||
}
|
||||
|
||||
public FloatElement(string caption, int layoutId)
|
||||
: base(caption, layoutId)
|
||||
{
|
||||
Value = 0;
|
||||
MinValue = 0;
|
||||
MaxValue = 10;
|
||||
}
|
||||
|
||||
public FloatElement(Bitmap left, Bitmap right, int value)
|
||||
: this(left, right, value, (int)DroidResources.ElementLayout.dialog_floatimage)
|
||||
{
|
||||
}
|
||||
|
||||
public FloatElement(Bitmap left, Bitmap right, int value, int layoutId)
|
||||
: base(string.Empty, layoutId)
|
||||
{
|
||||
Left = left;
|
||||
Right = right;
|
||||
MinValue = 0;
|
||||
MaxValue = 10;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public override View GetView(Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
TextView label;
|
||||
SeekBar slider;
|
||||
ImageView left;
|
||||
ImageView right;
|
||||
|
||||
View view = DroidResources.LoadFloatElementLayout(context, convertView, parent, LayoutId, out label, out slider, out left, out right);
|
||||
|
||||
if (view != null)
|
||||
{
|
||||
if (left != null)
|
||||
{
|
||||
if (Left != null)
|
||||
left.SetImageBitmap(Left);
|
||||
else
|
||||
left.Visibility = ViewStates.Gone;
|
||||
}
|
||||
if (right != null)
|
||||
{
|
||||
if (Right != null)
|
||||
right.SetImageBitmap(Right);
|
||||
else
|
||||
right.Visibility = ViewStates.Gone;
|
||||
}
|
||||
slider.Max = MaxValue - MinValue;
|
||||
slider.Progress = Value - MinValue;
|
||||
slider.SetOnSeekBarChangeListener(this);
|
||||
if (label != null)
|
||||
{
|
||||
if (ShowCaption)
|
||||
label.Text = Caption;
|
||||
else
|
||||
label.Visibility = ViewStates.Gone;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Android.Util.Log.Error("FloatElement", "GetView failed to load template view");
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
void SeekBar.IOnSeekBarChangeListener.OnProgressChanged(SeekBar seekBar, int progress, bool fromUser)
|
||||
{
|
||||
Value = MinValue + progress;
|
||||
}
|
||||
|
||||
void SeekBar.IOnSeekBarChangeListener.OnStartTrackingTouch(SeekBar seekBar)
|
||||
{
|
||||
}
|
||||
|
||||
void SeekBar.IOnSeekBarChangeListener.OnStopTrackingTouch(SeekBar seekBar)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
namespace MonoDroid.Dialog
|
||||
{
|
||||
/// <summary>
|
||||
/// Used by root elements to fetch information when they need to
|
||||
/// render a summary (Checkbox count or selected radio group).
|
||||
/// </summary>
|
||||
internal class Group
|
||||
{
|
||||
public string Key;
|
||||
|
||||
public Group(string key)
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures the information about mutually exclusive elements in a RootElement
|
||||
/// </summary>
|
||||
internal class RadioGroup : Group
|
||||
{
|
||||
public int Selected;
|
||||
|
||||
public RadioGroup(string key, int selected)
|
||||
: base(key)
|
||||
{
|
||||
Selected = selected;
|
||||
}
|
||||
|
||||
public RadioGroup(int selected)
|
||||
: base(null)
|
||||
{
|
||||
Selected = selected;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Views;
|
||||
using Android.Webkit;
|
||||
using Uri = Android.Net.Uri;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class HtmlElement : StringElement
|
||||
{
|
||||
// public string Value;
|
||||
|
||||
public HtmlElement(string caption, string url)
|
||||
: base(caption)
|
||||
{
|
||||
Url = Uri.Parse(url);
|
||||
}
|
||||
|
||||
public HtmlElement(string caption, Uri uri)
|
||||
: base(caption)
|
||||
{
|
||||
Url = uri;
|
||||
}
|
||||
|
||||
public Uri Url { get; set; }
|
||||
|
||||
void OpenUrl(Context context)
|
||||
{
|
||||
Intent intent = new Intent(context, typeof(HtmlActivity));
|
||||
intent.PutExtra("URL",this.Url.ToString());
|
||||
intent.PutExtra("Title",Caption);
|
||||
intent.AddFlags(ActivityFlags.NewTask);
|
||||
context.StartActivity(intent);
|
||||
}
|
||||
|
||||
public override View GetView(Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
View view = base.GetView (context, convertView, parent);
|
||||
|
||||
this.Click = (o, e) => OpenUrl(context);
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
[Activity]
|
||||
internal class HtmlActivity : Activity
|
||||
{
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
base.OnCreate(bundle);
|
||||
|
||||
Intent i = this.Intent;
|
||||
string url = i.GetStringExtra("URL");
|
||||
this.Title = i.GetStringExtra("Title");
|
||||
|
||||
WebView webview = new WebView(this);
|
||||
webview.Settings.BuiltInZoomControls = true;
|
||||
webview.Settings.JavaScriptEnabled = true;
|
||||
SetContentView(webview);
|
||||
webview.LoadUrl(url);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal interface IElementSizing
|
||||
{
|
||||
float GetHeight();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Graphics;
|
||||
using Android.Graphics.Drawables;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class ImageElement : Element
|
||||
{
|
||||
// Height for rows
|
||||
const int dimx = 48;
|
||||
const int dimy = 44;
|
||||
|
||||
// radius for rounding
|
||||
const int roundPx = 12;
|
||||
|
||||
public Bitmap Value
|
||||
{
|
||||
get { return _image; }
|
||||
set
|
||||
{
|
||||
_image = value;
|
||||
if (_imageView != null)
|
||||
{
|
||||
_imageView.SetImageBitmap(_image);
|
||||
}
|
||||
}
|
||||
}
|
||||
Bitmap _image;
|
||||
|
||||
ImageView _imageView;
|
||||
|
||||
public ImageElement(Bitmap image)
|
||||
: base("")
|
||||
{
|
||||
_image = image;
|
||||
}
|
||||
|
||||
protected override void Dispose (bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_image.Dispose();
|
||||
}
|
||||
base.Dispose (disposing);
|
||||
}
|
||||
|
||||
public override View GetView(Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
this.Click = delegate { SelectImage(); };
|
||||
|
||||
Bitmap scaledBitmap = Bitmap.CreateScaledBitmap(_image, dimx, dimy, true);
|
||||
|
||||
var view = convertView as RelativeLayout;
|
||||
if (view == null)
|
||||
{
|
||||
view = new RelativeLayout(context);
|
||||
_imageView = new ImageView(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
_imageView = (ImageView)view.GetChildAt(0);
|
||||
}
|
||||
_imageView.SetImageBitmap(scaledBitmap);
|
||||
|
||||
var parms = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
|
||||
parms.SetMargins(5, 2, 5, 2);
|
||||
parms.AddRule( LayoutRules.AlignParentLeft);
|
||||
if(_imageView.Parent != null && _imageView.Parent is ViewGroup)
|
||||
((ViewGroup)_imageView.Parent).RemoveView(_imageView);
|
||||
view.AddView(_imageView, parms);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void SelectImage()
|
||||
{
|
||||
Context context = GetContext();
|
||||
Activity activity = (Activity)context;
|
||||
Intent intent = new Intent(Intent.ActionPick, Android.Provider.MediaStore.Images.Media.InternalContentUri);
|
||||
activity.StartActivityForResult(intent,1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using Android.Content;
|
||||
using Android.Views;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class MultilineElement : EntryElement
|
||||
{
|
||||
public int Lines { get; set; }
|
||||
public int MaxLength {get;set;}
|
||||
|
||||
public MultilineElement(string caption, string value)
|
||||
: base(caption, value, (int)DroidResources.ElementLayout.dialog_textarea)
|
||||
{
|
||||
Lines = 3;
|
||||
}
|
||||
|
||||
public override View GetView(Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
View view = DroidResources.LoadMultilineElementLayout(context, convertView, parent, LayoutId, out _entry);
|
||||
if (_entry != null)
|
||||
{
|
||||
_entry.SetLines(Lines);
|
||||
_entry.Text = Value;
|
||||
_entry.Hint = Caption;
|
||||
_entry.TextChanged += delegate(object sender, Android.Text.TextChangedEventArgs e) {
|
||||
if(MaxLength > 0 && _entry.Text.Length > MaxLength)
|
||||
_entry.Text = _entry.Text.Substring(0,MaxLength);
|
||||
};
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using Android.Content;
|
||||
using Android.Views;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class RadioElement : StringElement
|
||||
{
|
||||
public string Group;
|
||||
internal int RadioIdx;
|
||||
|
||||
public RadioElement(string caption, string group)
|
||||
: base(caption)
|
||||
{
|
||||
Group = group;
|
||||
}
|
||||
|
||||
public RadioElement(string caption)
|
||||
: base(caption)
|
||||
{
|
||||
}
|
||||
|
||||
public override View GetView(Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
if (!(((RootElement)Parent.Parent)._group is RadioGroup))
|
||||
throw new Exception("The RootElement's Group is null or is not a RadioGroup");
|
||||
|
||||
return base.GetView(context, convertView, parent);
|
||||
}
|
||||
|
||||
public override string Summary()
|
||||
{
|
||||
return Caption;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
using System;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
|
||||
internal class EntryAttribute : Attribute
|
||||
{
|
||||
public string Placeholder;
|
||||
|
||||
public EntryAttribute() : this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public EntryAttribute(string placeholder)
|
||||
{
|
||||
Placeholder = placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
|
||||
internal class DateAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
|
||||
internal class TimeAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
|
||||
internal class CheckboxAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
|
||||
internal class MultilineAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
|
||||
internal class HtmlAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
|
||||
internal class SkipAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
|
||||
internal class StringAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
|
||||
internal class PasswordAttribute : EntryAttribute
|
||||
{
|
||||
public PasswordAttribute(string placeholder) : base(placeholder)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
|
||||
internal class AlignmentAttribute : Attribute
|
||||
{
|
||||
public AlignmentAttribute(object alignment)
|
||||
{
|
||||
Alignment = alignment;
|
||||
}
|
||||
public object Alignment;
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
|
||||
internal class RadioSelectionAttribute : Attribute
|
||||
{
|
||||
public string Target;
|
||||
|
||||
public RadioSelectionAttribute(string target)
|
||||
{
|
||||
Target = target;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
|
||||
internal class OnTapAttribute : Attribute
|
||||
{
|
||||
public string Method;
|
||||
|
||||
public OnTapAttribute(string method)
|
||||
{
|
||||
Method = method;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
|
||||
internal class CaptionAttribute : Attribute
|
||||
{
|
||||
public string Caption;
|
||||
|
||||
public CaptionAttribute(string caption)
|
||||
{
|
||||
Caption = caption;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
|
||||
internal class SectionAttribute : Attribute
|
||||
{
|
||||
public string Caption, Footer;
|
||||
|
||||
public SectionAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
public SectionAttribute(string caption)
|
||||
{
|
||||
Caption = caption;
|
||||
}
|
||||
|
||||
public SectionAttribute(string caption, string footer)
|
||||
{
|
||||
Caption = caption;
|
||||
Footer = footer;
|
||||
}
|
||||
}
|
||||
|
||||
internal class RangeAttribute : Attribute
|
||||
{
|
||||
public int High;
|
||||
public int Low;
|
||||
public bool ShowCaption;
|
||||
|
||||
public RangeAttribute(int low, int high)
|
||||
{
|
||||
Low = low;
|
||||
High = high;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,411 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class RootElement : Element, IEnumerable<Section>, IDialogInterfaceOnClickListener
|
||||
{
|
||||
TextView _caption;
|
||||
TextView _value;
|
||||
|
||||
int _summarySection, _summaryElement;
|
||||
internal Group _group;
|
||||
public bool UnevenRows;
|
||||
public Func<RootElement, View> _createOnSelected;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a RootSection with a caption
|
||||
/// </summary>
|
||||
/// <param name="caption">
|
||||
/// The caption to render.
|
||||
/// </param>
|
||||
public RootElement(string caption)
|
||||
: base(caption, (int) DroidResources.ElementLayout.dialog_root)
|
||||
{
|
||||
_summarySection = -1;
|
||||
Sections = new List<Section>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a RootSection with a caption and a callback that will
|
||||
/// create the nested UIViewController that is activated when the user
|
||||
/// taps on the element.
|
||||
/// </summary>
|
||||
/// <param name="caption">
|
||||
/// The caption to render.
|
||||
/// </param>
|
||||
public RootElement(string caption, Func<RootElement, View> createOnSelected)
|
||||
: base(caption, (int)DroidResources.ElementLayout.dialog_root)
|
||||
{
|
||||
_summarySection = -1;
|
||||
this._createOnSelected = createOnSelected;
|
||||
Sections = new List<Section>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a RootElement with a caption with a summary fetched from the specified section and leement
|
||||
/// </summary>
|
||||
/// <param name="caption">
|
||||
/// The caption to render cref="System.String"/>
|
||||
/// </param>
|
||||
/// <param name="section">
|
||||
/// The section that contains the element with the summary.
|
||||
/// </param>
|
||||
/// <param name="element">
|
||||
/// The element index inside the section that contains the summary for this RootSection.
|
||||
/// </param>
|
||||
public RootElement(string caption, int section, int element)
|
||||
: base(caption, (int)DroidResources.ElementLayout.dialog_root)
|
||||
{
|
||||
_summarySection = section;
|
||||
_summaryElement = element;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a RootElement that renders the summary based on the radio settings of the contained elements.
|
||||
/// </summary>
|
||||
/// <param name="caption">
|
||||
/// The caption to ender
|
||||
/// </param>
|
||||
/// <param name="group">
|
||||
/// The group that contains the checkbox or radio information. This is used to display
|
||||
/// the summary information when a RootElement is rendered inside a section.
|
||||
/// </param>
|
||||
public RootElement(string caption, Group group)
|
||||
: base(caption, (int)DroidResources.ElementLayout.dialog_root)
|
||||
{
|
||||
this._group = group;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Single save point for a context, elements can get this context via GetContext() for navigation operations
|
||||
/// </summary>
|
||||
public Context Context { get; set; }
|
||||
|
||||
internal List<Section> Sections = new List<Section>();
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return Sections.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public Section this[int idx]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Sections[idx];
|
||||
}
|
||||
}
|
||||
|
||||
internal int IndexOf(Section target)
|
||||
{
|
||||
int idx = 0;
|
||||
foreach (Section s in Sections)
|
||||
{
|
||||
if (s == target)
|
||||
return idx;
|
||||
idx++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
internal void Prepare()
|
||||
{
|
||||
int current = 0;
|
||||
foreach (Section s in Sections)
|
||||
{
|
||||
foreach (Element e in s.Elements)
|
||||
{
|
||||
var re = e as RadioElement;
|
||||
if (re != null)
|
||||
re.RadioIdx = current++;
|
||||
if (UnevenRows == false && e is IElementSizing)
|
||||
UnevenRows = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string Summary()
|
||||
{
|
||||
return GetSelectedValue();
|
||||
}
|
||||
|
||||
void SetSectionStartIndex()
|
||||
{
|
||||
int currentIndex = 0;
|
||||
foreach(var section in Sections)
|
||||
{
|
||||
section.StartIndex = currentIndex;
|
||||
currentIndex += section.Count;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds a new section to this RootElement
|
||||
/// </summary>
|
||||
/// <param name="section">
|
||||
/// The section to add, if the root is visible, the section is inserted with no animation
|
||||
/// </param>
|
||||
public void Add(Section section)
|
||||
{
|
||||
if (section == null)
|
||||
return;
|
||||
|
||||
Sections.Add(section);
|
||||
section.Parent = this;
|
||||
SetSectionStartIndex();
|
||||
}
|
||||
|
||||
//
|
||||
// This makes things LINQ friendly; You can now create RootElements
|
||||
// with an embedded LINQ expression, like this:
|
||||
// new RootElement ("Title") {
|
||||
// from x in names
|
||||
// select new Section (x) { new StringElement ("Sample") }
|
||||
//
|
||||
public void Add(IEnumerable<Section> sections)
|
||||
{
|
||||
foreach (var s in sections)
|
||||
Add(s);
|
||||
|
||||
SetSectionStartIndex();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new section into the RootElement
|
||||
/// </summary>
|
||||
/// <param name="idx">
|
||||
/// The index where the section is added <see cref="System.Int32"/>
|
||||
/// </param>
|
||||
/// <param name="newSections">
|
||||
/// A <see cref="Section[]"/> list of sections to insert
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// This inserts the specified list of sections (a params argument) into the
|
||||
/// root using the specified animation.
|
||||
/// </remarks>
|
||||
public void Insert(int idx, params Section[] newSections)
|
||||
{
|
||||
if (idx < 0 || idx > Sections.Count)
|
||||
return;
|
||||
if (newSections == null)
|
||||
return;
|
||||
|
||||
//if (Table != null)
|
||||
// Table.BeginUpdates();
|
||||
|
||||
int pos = idx;
|
||||
foreach (var s in newSections)
|
||||
{
|
||||
s.Parent = this;
|
||||
Sections.Insert(pos++, s);
|
||||
}
|
||||
|
||||
SetSectionStartIndex();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a section at a specified location
|
||||
/// </summary>
|
||||
public void RemoveAt(int idx)
|
||||
{
|
||||
if (idx < 0 || idx >= Sections.Count)
|
||||
return;
|
||||
|
||||
Sections.RemoveAt(idx);
|
||||
|
||||
SetSectionStartIndex();
|
||||
}
|
||||
|
||||
public void Remove(Section s)
|
||||
{
|
||||
if (s == null)
|
||||
return;
|
||||
int idx = Sections.IndexOf(s);
|
||||
if (idx == -1)
|
||||
return;
|
||||
RemoveAt(idx);
|
||||
|
||||
SetSectionStartIndex();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var s in Sections)
|
||||
s.Dispose();
|
||||
Sections = new List<Section>();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Context = null;
|
||||
if (Sections == null)
|
||||
return;
|
||||
Clear();
|
||||
Sections = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected Radio item in the whole Root.
|
||||
/// </summary>
|
||||
public int RadioSelected
|
||||
{
|
||||
get
|
||||
{
|
||||
var radio = _group as RadioGroup;
|
||||
if (radio != null)
|
||||
return radio.Selected;
|
||||
return -1;
|
||||
}
|
||||
set
|
||||
{
|
||||
var radio = _group as RadioGroup;
|
||||
if (radio != null)
|
||||
radio.Selected = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetSelectedValue()
|
||||
{
|
||||
var radio = _group as RadioGroup;
|
||||
if (radio == null)
|
||||
return string.Empty;
|
||||
|
||||
int selected = radio.Selected;
|
||||
int current = 0;
|
||||
string radioValue = string.Empty;
|
||||
foreach (var s in Sections)
|
||||
{
|
||||
foreach (var e in s.Elements)
|
||||
{
|
||||
if (!(e is RadioElement))
|
||||
continue;
|
||||
|
||||
if (current == selected)
|
||||
return e.Summary();
|
||||
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public override View GetView(Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
Context = context;
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.FromContext(context);
|
||||
|
||||
View cell = new TextView(context) {TextSize = 16f, Text = Caption};
|
||||
var radio = _group as RadioGroup;
|
||||
|
||||
if (radio != null)
|
||||
{
|
||||
string radioValue = GetSelectedValue();
|
||||
cell = DroidResources.LoadStringElementLayout(context, convertView, parent, LayoutId, out _caption, out _value);
|
||||
if (cell != null)
|
||||
{
|
||||
_caption.Text = Caption;
|
||||
_value.Text = radioValue;
|
||||
this.Click = (o, e) => { SelectRadio(); };
|
||||
}
|
||||
}
|
||||
else if (_group != null)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var s in Sections)
|
||||
{
|
||||
foreach (var e in s.Elements)
|
||||
{
|
||||
var ce = e as CheckboxElement;
|
||||
if (ce != null)
|
||||
{
|
||||
if (ce.Value)
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
var be = e as BoolElement;
|
||||
if (be != null)
|
||||
{
|
||||
if (be.Value)
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
//cell.DetailTextLabel.Text = count.ToString();
|
||||
}
|
||||
else if (_summarySection != -1 && _summarySection < Sections.Count)
|
||||
{
|
||||
var s = Sections[_summarySection];
|
||||
//if (summaryElement < s.Elements.Count)
|
||||
// cell.DetailTextLabel.Text = s.Elements[summaryElement].Summary();
|
||||
}
|
||||
//cell.Accessory = UITableViewCellAccessory.DisclosureIndicator;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
|
||||
public void SelectRadio()
|
||||
{
|
||||
List<string> items = new List<string>();
|
||||
foreach (var s in Sections)
|
||||
{
|
||||
foreach (var e in s.Elements)
|
||||
{
|
||||
if (e is RadioElement)
|
||||
items.Add(e.Summary());
|
||||
}
|
||||
}
|
||||
|
||||
var dialog = new AlertDialog.Builder(Context);
|
||||
dialog.SetSingleChoiceItems(items.ToArray(), this.RadioSelected, this);
|
||||
dialog.SetTitle(this.Caption);
|
||||
dialog.SetNegativeButton("Cancel", this);
|
||||
dialog.Create().Show();
|
||||
}
|
||||
|
||||
void IDialogInterfaceOnClickListener.OnClick(IDialogInterface dialog, int which)
|
||||
{
|
||||
if ((int)which >= 0)
|
||||
{
|
||||
this.RadioSelected = (int)which;
|
||||
string radioValue = GetSelectedValue();
|
||||
_value.Text = radioValue;
|
||||
}
|
||||
|
||||
dialog.Dismiss();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerator that returns all the sections in the RootElement.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="IEnumerator"/>
|
||||
/// </returns>
|
||||
public IEnumerator<Section> GetEnumerator()
|
||||
{
|
||||
return Sections.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerator that returns all the sections in the RootElement.
|
||||
/// </summary>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return Sections.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,387 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Android.Content;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Orientation = Android.Widget.Orientation;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
/// <summary>
|
||||
/// Sections contain individual Element instances that are rendered by MonoDroid.Dialog
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Sections are used to group elements in the screen and they are the
|
||||
/// only valid direct child of the RootElement. Sections can contain
|
||||
/// any of the standard elements, including new RootElements.
|
||||
///
|
||||
/// RootElements embedded in a section are used to navigate to a new
|
||||
/// deeper level.
|
||||
///
|
||||
/// You can assign a header and a footer either as strings (Header and Footer)
|
||||
/// properties, or as Views to be shown (HeaderView and FooterView). Internally
|
||||
/// this uses the same storage, so you can only show one or the other.
|
||||
/// </remarks>
|
||||
internal class Section : Element, IEnumerable<Element>
|
||||
{
|
||||
public List<Element> Elements = new List<Element>();
|
||||
|
||||
List<string> ElementTypes = new List<string>();
|
||||
|
||||
public SectionAdapter Adapter;
|
||||
public int StartIndex {get;set;}
|
||||
// X corresponds to the alignment, Y to the height of the password
|
||||
private object footer;
|
||||
private object header;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a Section without header or footers.
|
||||
/// </summary>
|
||||
public Section()
|
||||
: this("")
|
||||
{
|
||||
Adapter = new SectionAdapter(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a Section with the specified header
|
||||
/// </summary>
|
||||
/// <param name="caption">
|
||||
/// The header to display
|
||||
/// </param>
|
||||
public Section(string caption)
|
||||
: base(caption)
|
||||
{
|
||||
Adapter = new SectionAdapter(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a Section with a header and a footer
|
||||
/// </summary>
|
||||
/// <param name="caption">
|
||||
/// The caption to display (or null to not display a caption)
|
||||
/// </param>
|
||||
/// <param name="footer">
|
||||
/// The footer to display.
|
||||
/// </param>
|
||||
public Section(string caption, string footer)
|
||||
: this(caption)
|
||||
{
|
||||
Footer = footer;
|
||||
}
|
||||
|
||||
public Section(View header)
|
||||
: this()
|
||||
{
|
||||
HeaderView = header;
|
||||
}
|
||||
|
||||
public Section(View header, View footer)
|
||||
: this()
|
||||
{
|
||||
HeaderView = header;
|
||||
FooterView = footer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The section header, as a string
|
||||
/// </summary>
|
||||
public string Header
|
||||
{
|
||||
get { return Caption; }
|
||||
set { Caption = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The section footer, as a string.
|
||||
/// </summary>
|
||||
public string Footer
|
||||
{
|
||||
get { return footer as string; }
|
||||
set { footer = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The section's header view.
|
||||
/// </summary>
|
||||
public View HeaderView
|
||||
{
|
||||
get { return header as View; }
|
||||
set { header = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The section's footer view.
|
||||
/// </summary>
|
||||
public View FooterView
|
||||
{
|
||||
get { return footer as View; }
|
||||
set { footer = value; }
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return Elements.Count; }
|
||||
}
|
||||
|
||||
public Element this[int idx]
|
||||
{
|
||||
get { return Elements[idx]; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new child Element to the Section
|
||||
/// </summary>
|
||||
/// <param name="element">
|
||||
/// An element to add to the section.
|
||||
/// </param>
|
||||
public void Add(Element element)
|
||||
{
|
||||
if (element == null)
|
||||
return;
|
||||
|
||||
var elementType = element.GetType().FullName;
|
||||
|
||||
if (!ElementTypes.Contains(elementType))
|
||||
ElementTypes.Add(elementType);
|
||||
|
||||
Elements.Add(element);
|
||||
element.Parent = this;
|
||||
|
||||
if (Parent != null)
|
||||
InsertVisual(Elements.Count - 1, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add version that can be used with LINQ
|
||||
/// </summary>
|
||||
/// <param name="elements">
|
||||
/// An enumerable list that can be produced by something like:
|
||||
/// from x in ... select (Element) new MyElement (...)
|
||||
/// </param>
|
||||
public int Add(IEnumerable<Element> elements)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (Element e in elements)
|
||||
{
|
||||
Add(e);
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use to add a View to a section, it makes the section opaque, to
|
||||
/// get a transparent one, you must manually call ViewElement
|
||||
public void Add(View view)
|
||||
{
|
||||
if (view == null)
|
||||
return;
|
||||
Add(new ViewElement(null, view, false));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Views to the section.
|
||||
/// </summary>
|
||||
/// <param name="views">
|
||||
/// An enumarable list that can be produced by something like:
|
||||
/// from x in ... select (View) new UIFoo ();
|
||||
/// </param>
|
||||
public void Add(IEnumerable<View> views)
|
||||
{
|
||||
foreach (View v in views)
|
||||
Add(v);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a series of elements into the Section using the specified animation
|
||||
/// </summary>
|
||||
/// <param name="idx">
|
||||
/// The index where the elements are inserted
|
||||
/// </param>
|
||||
/// <param name="anim">
|
||||
/// The animation to use
|
||||
/// </param>
|
||||
/// <param name="newElements">
|
||||
/// A series of elements.
|
||||
/// </param>
|
||||
public void Insert(int idx, params Element[] newElements)
|
||||
{
|
||||
if (newElements == null)
|
||||
return;
|
||||
|
||||
int pos = idx;
|
||||
foreach (Element e in newElements)
|
||||
{
|
||||
Elements.Insert(pos++, e);
|
||||
e.Parent = this;
|
||||
}
|
||||
var root = Parent as RootElement;
|
||||
if (Parent != null)
|
||||
{
|
||||
InsertVisual(idx, newElements.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public int Insert(int idx, IEnumerable<Element> newElements)
|
||||
{
|
||||
if (newElements == null)
|
||||
return 0;
|
||||
|
||||
int pos = idx;
|
||||
int count = 0;
|
||||
foreach (Element e in newElements)
|
||||
{
|
||||
Elements.Insert(pos++, e);
|
||||
e.Parent = this;
|
||||
count++;
|
||||
}
|
||||
var root = Parent as RootElement;
|
||||
if (root != null )
|
||||
{
|
||||
InsertVisual(idx, pos - idx);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private void InsertVisual(int idx, int count)
|
||||
{
|
||||
//var root = Parent as RootElement;
|
||||
|
||||
//if (root == null || root.TableView == null)
|
||||
// return;
|
||||
|
||||
//int sidx = root.IndexOf(this);
|
||||
//var paths = new NSIndexPath[count];
|
||||
//for (int i = 0; i < count; i++)
|
||||
// paths[i] = NSIndexPath.FromRowSection(idx + i, sidx);
|
||||
|
||||
//root.TableView.InsertRows(paths);
|
||||
}
|
||||
|
||||
public void Remove(Element e)
|
||||
{
|
||||
if (e == null)
|
||||
return;
|
||||
for (int i = Elements.Count; i > 0;)
|
||||
{
|
||||
i--;
|
||||
if (Elements[i] == e)
|
||||
{
|
||||
RemoveRange(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(int idx)
|
||||
{
|
||||
RemoveRange(idx, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a range of elements from the Section
|
||||
/// </summary>
|
||||
/// <param name="start">
|
||||
/// Starting position
|
||||
/// </param>
|
||||
/// <param name="count">
|
||||
/// Number of elements to remove from the section
|
||||
/// </param>
|
||||
public void RemoveRange(int start, int count)
|
||||
{
|
||||
if (start < 0 || start >= Elements.Count)
|
||||
return;
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
var root = Parent as RootElement;
|
||||
|
||||
if (start + count > Elements.Count)
|
||||
count = Elements.Count - start;
|
||||
|
||||
Elements.RemoveRange(start, count);
|
||||
|
||||
if (root == null)
|
||||
return;
|
||||
|
||||
int sidx = root.IndexOf(this);
|
||||
//var paths = new NSIndexPath[count];
|
||||
//for (int i = 0; i < count; i++)
|
||||
// paths[i] = NSIndexPath.FromRowSection(start + i, sidx);
|
||||
//root.TableView.DeleteRows(paths, anim);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
foreach (Element e in Elements)
|
||||
e.Dispose();
|
||||
Elements = new List<Element>();
|
||||
|
||||
var root = Parent as RootElement;
|
||||
//if (root != null && root.TableView != null)
|
||||
// root.TableView.ReloadData();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Parent = null;
|
||||
Clear();
|
||||
Elements = null;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetElementViewType(Element e)
|
||||
{
|
||||
var elementType = e.GetType().FullName;
|
||||
|
||||
for (int i = 0; i < ElementTypes.Count; i++)
|
||||
{
|
||||
if (ElementTypes[i].Equals(elementType))
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int ElementViewTypeCount
|
||||
{
|
||||
get { return ElementTypes.Count; }
|
||||
}
|
||||
|
||||
public override View GetView(Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
TextView view = (convertView as TextView)
|
||||
?? new TextView(context, null, Android.Resource.Attribute.ListSeparatorTextViewStyle);
|
||||
|
||||
view.Text = this.Caption;
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/// Enumerator to get all the elements in the Section.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="IEnumerator{T}"/>
|
||||
/// </returns>
|
||||
public IEnumerator<Element> GetEnumerator()
|
||||
{
|
||||
foreach (Element e in Elements)
|
||||
yield return e;
|
||||
}
|
||||
|
||||
/// Enumerator to get all the elements in the Section.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="IEnumerator{T}"/>
|
||||
/// </returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
foreach (Element e in Elements)
|
||||
yield return e;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class SectionAdapter : BaseAdapter<Element>
|
||||
{
|
||||
public SectionAdapter(Section section)
|
||||
{
|
||||
this.Section = section;
|
||||
}
|
||||
|
||||
public Context Context { get; set; }
|
||||
|
||||
public Section Section { get; private set; }
|
||||
|
||||
public override int Count
|
||||
{
|
||||
get { return this.Section.Elements.Count; }
|
||||
}
|
||||
|
||||
public override Element this[int position]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Section.Elements[position];
|
||||
}
|
||||
}
|
||||
|
||||
public override int ViewTypeCount
|
||||
{
|
||||
get { return this.Section.ElementViewTypeCount; }
|
||||
}
|
||||
|
||||
public override int GetItemViewType(int position)
|
||||
{
|
||||
return this.Section.GetElementViewType(this[position]);
|
||||
}
|
||||
|
||||
public override long GetItemId(int position)
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
public override View GetView(int position, View convertView, ViewGroup parent)
|
||||
{
|
||||
return Section.Elements[position].GetView(this.Context, convertView, parent);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
|
||||
using System.Linq;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Renderscripts;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Android.Runtime;
|
||||
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class StringElement : Element
|
||||
{
|
||||
|
||||
|
||||
public int? FontSize {get;set;}
|
||||
public string Value
|
||||
{
|
||||
get { return _value; }
|
||||
set
|
||||
{
|
||||
_value = value;
|
||||
if (_text != null)
|
||||
_text.Text = _value;
|
||||
|
||||
}
|
||||
}
|
||||
private string _value;
|
||||
|
||||
public object Alignment;
|
||||
|
||||
public StringElement(string caption)
|
||||
: base(caption, (int)DroidResources.ElementLayout.dialog_labelfieldright)
|
||||
{
|
||||
}
|
||||
|
||||
public StringElement(string caption, int layoutId)
|
||||
: base(caption, layoutId)
|
||||
{
|
||||
}
|
||||
|
||||
public StringElement(string caption, string value)
|
||||
: base(caption, (int)DroidResources.ElementLayout.dialog_labelfieldright)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
|
||||
public StringElement(string caption, string value, EventHandler clicked)
|
||||
: base(caption, (int)DroidResources.ElementLayout.dialog_labelfieldright)
|
||||
{
|
||||
Value = value;
|
||||
this.Click = clicked;
|
||||
}
|
||||
|
||||
public StringElement(string caption, string value, int layoutId)
|
||||
: base(caption, layoutId)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public StringElement(string caption, EventHandler clicked)
|
||||
: base(caption, (int)DroidResources.ElementLayout.dialog_labelfieldright)
|
||||
{
|
||||
Value = null;
|
||||
this.Click = clicked;
|
||||
}
|
||||
|
||||
public override View GetView(Context context, View convertView, ViewGroup parent)
|
||||
{
|
||||
View view = DroidResources.LoadStringElementLayout(context, convertView, parent, LayoutId, out _caption, out _text);
|
||||
if (view != null)
|
||||
{
|
||||
_caption.Text = Caption;
|
||||
_text.Text = Value;
|
||||
|
||||
if (FontSize.HasValue)
|
||||
{
|
||||
_caption.TextSize = FontSize.Value;
|
||||
_text.TextSize = FontSize.Value;
|
||||
}
|
||||
|
||||
if (Click != null)
|
||||
view.Click += Click;
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
public override string Summary()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public override bool Matches(string text)
|
||||
{
|
||||
return (Value != null && Value.IndexOf(text, StringComparison.CurrentCultureIgnoreCase) != -1) ||
|
||||
base.Matches(text);
|
||||
}
|
||||
|
||||
protected TextView _caption;
|
||||
protected TextView _text;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
//_caption.Dispose();
|
||||
_caption = null;
|
||||
//_text.Dispose();
|
||||
_text = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Android.Views;
|
||||
|
||||
namespace MonoDroid.Dialog
|
||||
{
|
||||
internal class ViewElement : IEnumerable<View>
|
||||
{
|
||||
public ViewElement(object o, View view, bool b)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public IEnumerator<View> GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Android.OS;
|
||||
using Xunit;
|
||||
using Xunit.Runners.UI;
|
||||
|
||||
namespace $rootnamespace$
|
||||
{
|
||||
[Activity(Label = "xUnit Android Runner", MainLauncher = true)]
|
||||
public class MainActivity : RunnerActivity
|
||||
{
|
||||
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
// tests can be inside the main assembly
|
||||
Add(Assembly.GetExecutingAssembly());
|
||||
|
||||
AddExecutionAssembly(typeof(ExceptionUtility).Assembly);
|
||||
// or in any reference assemblies
|
||||
// Add(typeof(m4a.tests.RunnerTest).Assembly);
|
||||
// or in any assembly that you load (since JIT is available)
|
||||
|
||||
#if false
|
||||
// you can use the default or set your own custom writer (e.g. save to web site and tweet it ;-)
|
||||
Runner.Writer = new TcpTextWriter ("10.0.1.2", 16384);
|
||||
// start running the test suites as soon as the application is loaded
|
||||
Runner.AutoStart = true;
|
||||
// crash the application (to ensure it's ended) and return to springboard
|
||||
Runner.TerminateAfterExecution = true;
|
||||
#endif
|
||||
// you cannot add more assemblies once calling base
|
||||
base.OnCreate(bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Android.App;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("xunit.runner.android")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("xunit.runner.android")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
|
||||
// Add some common permissions, these can be removed if not needed
|
||||
[assembly: UsesPermission(Android.Manifest.Permission.Internet)]
|
||||
[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)]
|
|
@ -0,0 +1,50 @@
|
|||
Images, layout descriptions, binary blobs and string dictionaries can be included
|
||||
in your application as resource files. Various Android APIs are designed to
|
||||
operate on the resource IDs instead of dealing with images, strings or binary blobs
|
||||
directly.
|
||||
|
||||
For example, a sample Android app that contains a user interface layout (main.xml),
|
||||
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
|
||||
would keep its resources in the "Resources" directory of the application:
|
||||
|
||||
Resources/
|
||||
drawable-hdpi/
|
||||
icon.png
|
||||
|
||||
drawable-ldpi/
|
||||
icon.png
|
||||
|
||||
drawable-mdpi/
|
||||
icon.png
|
||||
|
||||
layout/
|
||||
main.xml
|
||||
|
||||
values/
|
||||
strings.xml
|
||||
|
||||
In order to get the build system to recognize Android resources, set the build action to
|
||||
"AndroidResource". The native Android APIs do not operate directly with filenames, but
|
||||
instead operate on resource IDs. When you compile an Android application that uses resources,
|
||||
the build system will package the resources for distribution and generate a class called
|
||||
"Resource" that contains the tokens for each one of the resources included. For example,
|
||||
for the above Resources layout, this is what the Resource class would expose:
|
||||
|
||||
public class Resource {
|
||||
public class drawable {
|
||||
public const int icon = 0x123;
|
||||
}
|
||||
|
||||
public class layout {
|
||||
public const int main = 0x456;
|
||||
}
|
||||
|
||||
public class strings {
|
||||
public const int first_string = 0xabc;
|
||||
public const int second_string = 0xbcd;
|
||||
}
|
||||
}
|
||||
|
||||
You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main
|
||||
to reference the layout/main.xml file, or Resource.strings.first_string to reference the first
|
||||
string in the dictionary file values/strings.xml.
|
|
@ -0,0 +1,182 @@
|
|||
#pragma warning disable 1591
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.34209
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[assembly: global::Android.Runtime.ResourceDesignerAttribute("Xunit.Runners.Resource", IsApplication=false)]
|
||||
|
||||
namespace Xunit.Runners
|
||||
{
|
||||
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
|
||||
public partial class Resource
|
||||
{
|
||||
|
||||
static Resource()
|
||||
{
|
||||
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||
}
|
||||
|
||||
public partial class Attribute
|
||||
{
|
||||
|
||||
static Attribute()
|
||||
{
|
||||
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||
}
|
||||
|
||||
private Attribute()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Drawable
|
||||
{
|
||||
|
||||
// aapt resource value: 0x7f020000
|
||||
public static int dialog_disclosure = 2130837504;
|
||||
|
||||
// aapt resource value: 0x7f020001
|
||||
public static int dialog_expander_ic_minimized = 2130837505;
|
||||
|
||||
static Drawable()
|
||||
{
|
||||
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||
}
|
||||
|
||||
private Drawable()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Id
|
||||
{
|
||||
|
||||
// aapt resource value: 0x7f040002
|
||||
public static int dialog_BoolField = 2130968578;
|
||||
|
||||
// aapt resource value: 0x7f040003
|
||||
public static int dialog_Button = 2130968579;
|
||||
|
||||
// aapt resource value: 0x7f040009
|
||||
public static int dialog_DisclosureField = 2130968585;
|
||||
|
||||
// aapt resource value: 0x7f040005
|
||||
public static int dialog_ImageLeft = 2130968581;
|
||||
|
||||
// aapt resource value: 0x7f040007
|
||||
public static int dialog_ImageRight = 2130968583;
|
||||
|
||||
// aapt resource value: 0x7f040000
|
||||
public static int dialog_LabelField = 2130968576;
|
||||
|
||||
// aapt resource value: 0x7f040001
|
||||
public static int dialog_LabelSubtextField = 2130968577;
|
||||
|
||||
// aapt resource value: 0x7f040008
|
||||
public static int dialog_Panel = 2130968584;
|
||||
|
||||
// aapt resource value: 0x7f04000a
|
||||
public static int dialog_RadioButtonList = 2130968586;
|
||||
|
||||
// aapt resource value: 0x7f040006
|
||||
public static int dialog_SliderField = 2130968582;
|
||||
|
||||
// aapt resource value: 0x7f04000b
|
||||
public static int dialog_Spinner = 2130968587;
|
||||
|
||||
// aapt resource value: 0x7f040004
|
||||
public static int dialog_ValueField = 2130968580;
|
||||
|
||||
// aapt resource value: 0x7f04000c
|
||||
public static int iFormFieldValue = 2130968588;
|
||||
|
||||
static Id()
|
||||
{
|
||||
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||
}
|
||||
|
||||
private Id()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Layout
|
||||
{
|
||||
|
||||
// aapt resource value: 0x7f030000
|
||||
public static int dialog_achievements = 2130903040;
|
||||
|
||||
// aapt resource value: 0x7f030001
|
||||
public static int dialog_boolfieldleft = 2130903041;
|
||||
|
||||
// aapt resource value: 0x7f030002
|
||||
public static int dialog_boolfieldright = 2130903042;
|
||||
|
||||
// aapt resource value: 0x7f030003
|
||||
public static int dialog_boolfieldsubleft = 2130903043;
|
||||
|
||||
// aapt resource value: 0x7f030004
|
||||
public static int dialog_boolfieldsubright = 2130903044;
|
||||
|
||||
// aapt resource value: 0x7f030005
|
||||
public static int dialog_button = 2130903045;
|
||||
|
||||
// aapt resource value: 0x7f030006
|
||||
public static int dialog_datefield = 2130903046;
|
||||
|
||||
// aapt resource value: 0x7f030007
|
||||
public static int dialog_fieldsetlabel = 2130903047;
|
||||
|
||||
// aapt resource value: 0x7f030008
|
||||
public static int dialog_floatimage = 2130903048;
|
||||
|
||||
// aapt resource value: 0x7f030009
|
||||
public static int dialog_labelfieldbelow = 2130903049;
|
||||
|
||||
// aapt resource value: 0x7f03000a
|
||||
public static int dialog_labelfieldright = 2130903050;
|
||||
|
||||
// aapt resource value: 0x7f03000b
|
||||
public static int dialog_onofffieldright = 2130903051;
|
||||
|
||||
// aapt resource value: 0x7f03000c
|
||||
public static int dialog_panel = 2130903052;
|
||||
|
||||
// aapt resource value: 0x7f03000d
|
||||
public static int dialog_root = 2130903053;
|
||||
|
||||
// aapt resource value: 0x7f03000e
|
||||
public static int dialog_selectlist = 2130903054;
|
||||
|
||||
// aapt resource value: 0x7f03000f
|
||||
public static int dialog_selectlistfield = 2130903055;
|
||||
|
||||
// aapt resource value: 0x7f030010
|
||||
public static int dialog_textarea = 2130903056;
|
||||
|
||||
// aapt resource value: 0x7f030011
|
||||
public static int dialog_textfieldbelow = 2130903057;
|
||||
|
||||
// aapt resource value: 0x7f030012
|
||||
public static int dialog_textfieldright = 2130903058;
|
||||
|
||||
static Layout()
|
||||
{
|
||||
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||
}
|
||||
|
||||
private Layout()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 289 B |
Двоичные данные
src/xunit.runner.android/Resources/drawable/dialog_expander_ic_minimized.9.png
Normal file
Двоичные данные
src/xunit.runner.android/Resources/drawable/dialog_expander_ic_minimized.9.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 2.9 KiB |
|
@ -0,0 +1,67 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1"
|
||||
>
|
||||
<TextView android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:layout_marginTop="9dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:text="FieldLabel"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
/>
|
||||
<TextView android:id="@+id/dialog_LabelSubtextField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="Subtext"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
/>
|
||||
</LinearLayout>
|
||||
<!--LinearLayout>
|
||||
<ImageView
|
||||
android:id="@+id/dialog_ImageRight"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right|center_vertical"
|
||||
android:src="@drawable/dialog_disclosure"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginRight="3dp"
|
||||
/>
|
||||
<TextView android:id="@+id/dialog_LabelPercentageField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right|center_vertical"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="0"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
/>
|
||||
</LinearLayout-->
|
||||
<CheckBox
|
||||
android:id="@+id/dialog_BoolField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_gravity="right"
|
||||
android:layout_marginRight="8dp"
|
||||
android:focusable="false"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:background="@android:color/transparent"
|
||||
>
|
||||
<CheckBox
|
||||
android:id="@+id/dialog_BoolField"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="0"
|
||||
/>
|
||||
<TextView android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:paddingTop="5dp"
|
||||
android:text="TitleText"
|
||||
android:textSize="?android:attr/textAppearanceLarge"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="horizontal">
|
||||
<TextView
|
||||
android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="Label"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:gravity="left|center_vertical"
|
||||
android:layout_weight="1.0"
|
||||
/>
|
||||
<CheckBox
|
||||
android:id="@+id/dialog_BoolField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_gravity="right"
|
||||
android:layout_marginRight="8dp"
|
||||
android:focusable="false"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
>
|
||||
<CheckBox
|
||||
android:id="@+id/dialog_BoolField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginRight="12dp"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/dialog_LabelSubtextField"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toRightOf="@id/dialog_BoolField"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="SubText"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toRightOf="@id/dialog_BoolField"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_above="@id/dialog_LabelSubtextField"
|
||||
android:layout_alignWithParentIfMissing="true"
|
||||
android:gravity="center_vertical"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:layout_marginTop="-6dp"
|
||||
android:text="TitleText"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
/>
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1"
|
||||
>
|
||||
<TextView android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:layout_marginTop="9dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:text="FieldLabel"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
/>
|
||||
<TextView android:id="@+id/dialog_LabelSubtextField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="Subtext"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
/>
|
||||
</LinearLayout>
|
||||
<CheckBox
|
||||
android:id="@+id/dialog_BoolField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_gravity="right"
|
||||
android:layout_marginRight="8dp"
|
||||
android:focusable="false"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"
|
||||
>
|
||||
<Button
|
||||
android:id="@+id/dialog_Button"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="18sp"
|
||||
android:text="Submit"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:padding="12dp"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
<TextView android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="-6dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:textSize="16sp"
|
||||
android:textColor="#ffffff"
|
||||
/>
|
||||
<DatePicker
|
||||
android:id="@+id/dialog_ValueField"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp"
|
||||
>
|
||||
<TextView android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="Fieldset Label"
|
||||
android:textSize="21sp"
|
||||
android:textColor="#ffffff"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
<TextView
|
||||
android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="Fieldset Label"
|
||||
android:textStyle="bold"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
/>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingLeft="3dp"
|
||||
android:paddingRight="3dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
<ImageView
|
||||
android:id="@+id/dialog_ImageLeft"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left|center_vertical"
|
||||
android:src="@drawable/dialog_disclosure"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginRight="3dp"
|
||||
/>
|
||||
<SeekBar android:id="@+id/dialog_SliderField"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
<ImageView
|
||||
android:id="@+id/dialog_ImageRight"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right|center_vertical"
|
||||
android:src="@drawable/dialog_disclosure"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginRight="3dp"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
<TextView android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="Fieldset Label"
|
||||
android:textStyle="bold"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/dialog_ValueField"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:text="FieldValue"
|
||||
android:textColor="#000000"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="8dp"
|
||||
>
|
||||
<TextView
|
||||
android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="Label"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:gravity="left|center_vertical"
|
||||
android:layout_weight="1.0"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/dialog_ValueField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_marginRight="8dp"
|
||||
android:maxWidth="350dp"
|
||||
android:text="FieldValue"
|
||||
android:singleLine="true"
|
||||
android:gravity="center_vertical"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:ellipsize="end"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
<TextView
|
||||
android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="Label"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:gravity="left|center_vertical"
|
||||
android:layout_weight="1.0"
|
||||
/>
|
||||
<ToggleButton
|
||||
android:id="@+id/dialog_BoolField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_gravity="right"
|
||||
android:focusable="false"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/dialog_Panel"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
>
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1"
|
||||
>
|
||||
<TextView android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="Fieldset Label"
|
||||
android:textStyle="bold"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/dialog_ValueField"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:singleLine="true"
|
||||
android:text="FieldValue"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
/>
|
||||
</LinearLayout>
|
||||
<ImageView
|
||||
android:id="@+id/dialog_DisclosureField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/dialog_expander_ic_minimized"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:padding="12dp"
|
||||
>
|
||||
<TextView android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight=".99"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:paddingTop="5dp"
|
||||
android:text="TitleText"
|
||||
android:textSize="21sp"
|
||||
android:textColor="#ffffff"
|
||||
/>
|
||||
<RadioButton
|
||||
android:id="@+id/dialog_RadioButtonList"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight=".01"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="horizontal"
|
||||
android:padding="8dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
>
|
||||
<TextView android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:text="Fieldset Label"
|
||||
android:textSize="21sp"
|
||||
android:textColor="#ffffff"
|
||||
/>
|
||||
<Spinner
|
||||
android:id="@+id/dialog_Spinner"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="left"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/iFormFieldValue"
|
||||
android:padding="12dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxWidth="350dp"
|
||||
android:gravity="top"
|
||||
android:lines="5"
|
||||
android:inputType="text|textMultiLine"
|
||||
android:textSize="21sp"
|
||||
android:background="@android:color/transparent"
|
||||
android:textColor="#ff000000"/>
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
<TextView android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="Fieldset Label"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textStyle="bold"
|
||||
/>
|
||||
<EditText
|
||||
android:id="@+id/dialog_ValueField"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="2dp"
|
||||
android:singleLine="true"
|
||||
android:text="FieldValue"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
>
|
||||
<TextView android:id="@+id/dialog_LabelField"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:gravity="left|center_vertical"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="Label"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
/>
|
||||
<EditText
|
||||
android:id="@+id/dialog_ValueField"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxWidth="350dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:singleLine="true"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Value"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
|
||||
namespace Xunit.Runners
|
||||
{
|
||||
public class RunnerOptions
|
||||
{
|
||||
public RunnerOptions()
|
||||
{
|
||||
}
|
||||
|
||||
private static RunnerOptions current;
|
||||
|
||||
public static RunnerOptions Current { get { return current; }}
|
||||
|
||||
// Options are not as useful as under iOS since re-installing the
|
||||
// application deletes the file containing them.
|
||||
internal RunnerOptions(Activity activity)
|
||||
{
|
||||
var prefs = activity.GetSharedPreferences("options", FileCreationMode.Private);
|
||||
EnableNetwork = prefs.GetBoolean("remote", false);
|
||||
HostName = prefs.GetString("hostName", "0.0.0.0");
|
||||
HostPort = prefs.GetInt("hostPort", -1);
|
||||
NameDisplay = (NameDisplay)prefs.GetInt("nameDisplay", 1);
|
||||
ParallelizeAssemblies = prefs.GetBoolean("parallel", false);
|
||||
|
||||
current = this;
|
||||
}
|
||||
|
||||
public bool EnableNetwork { get; set; }
|
||||
|
||||
public string HostName { get; set; }
|
||||
public bool ParallelizeAssemblies { get; set; }
|
||||
public int HostPort { get; set; }
|
||||
|
||||
public bool ShowUseNetworkLogger
|
||||
{
|
||||
get { return (EnableNetwork && !String.IsNullOrWhiteSpace(HostName) && (HostPort > 0)); }
|
||||
}
|
||||
|
||||
public void Save(Activity activity)
|
||||
{
|
||||
var prefs = activity.GetSharedPreferences("options", FileCreationMode.Private);
|
||||
var edit = prefs.Edit();
|
||||
edit.PutBoolean("remote", EnableNetwork);
|
||||
edit.PutString("hostName", HostName);
|
||||
edit.PutInt("hostPort", HostPort);
|
||||
edit.PutInt("nameDisplay", (int)NameDisplay);
|
||||
edit.PutBoolean("parallel", ParallelizeAssemblies);
|
||||
edit.Commit();
|
||||
}
|
||||
|
||||
public static void Initialize(Activity activity)
|
||||
{
|
||||
// New it up to read the latest prefs
|
||||
current = new RunnerOptions(activity);
|
||||
}
|
||||
|
||||
public static string GetDisplayName(string displayName, string shortMethodName, string fullyQualifiedMethodName)
|
||||
{
|
||||
|
||||
if (current.NameDisplay == NameDisplay.Full)
|
||||
return displayName;
|
||||
if (displayName == fullyQualifiedMethodName || displayName.StartsWith(fullyQualifiedMethodName + "("))
|
||||
return shortMethodName + displayName.Substring(fullyQualifiedMethodName.Length);
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public NameDisplay NameDisplay { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="xunit.abstractions" version="2.0.0-beta-build2664" targetFramework="MonoAndroid22" />
|
||||
<package id="xunit.runner.utility" version="2.0.0-beta-build2664" targetFramework="MonoAndroid22" />
|
||||
</packages>
|
|
@ -0,0 +1,132 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{A0D90D0B-BC16-42AA-AF74-8B49772F8406}</ProjectGuid>
|
||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Xunit.Runners</RootNamespace>
|
||||
<AssemblyName>xunit.runner.android</AssemblyName>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
|
||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidLinkMode>None</AndroidLinkMode>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Android" />
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\xunit.abstractions.2.0.0-beta-build2664\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid\xunit.abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="xunit.runner.utility, Version=2.0.0.2664, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\xunit.runner.utility.2.0.0-beta-build2664\lib\monoandroid\xunit.runner.utility.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Activities\CreditsActivity.cs" />
|
||||
<Compile Include="Activities\OptionsActivity.cs" />
|
||||
<Compile Include="Activities\RunnerActivity.cs" />
|
||||
<Compile Include="Activities\TestResultActivity.cs" />
|
||||
<Compile Include="Activities\TestSuiteActivity.cs" />
|
||||
<Compile Include="AndroidRunner.cs" />
|
||||
<Compile Include="Elements\ActionElement.cs" />
|
||||
<Compile Include="Elements\ActivityElement.cs" />
|
||||
<Compile Include="Elements\FormattedElement.cs" />
|
||||
<Compile Include="Elements\FormattedSection.cs" />
|
||||
<Compile Include="Elements\TestCaseElement.cs" />
|
||||
<Compile Include="Elements\TestElement.cs" />
|
||||
<Compile Include="Elements\TestSuiteElement.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\BindingContext.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\BooleanElement.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\ButtonElement.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\CheckboxElement.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\DateTimeElement.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\DialogActivity.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\DialogAdapter.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\DialogHelper.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\DroidResources.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\Element.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\EntryElement.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\FloatElement.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\Group.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\HtmlElement.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\IElementSizing.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\ImageElement.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\MultilineElement.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\RadioElement.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\Reflect.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\RootElement.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\Section.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\SectionAdapter.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\StringElement.cs" />
|
||||
<Compile Include="MonoTouch.Dialog\ViewElement.cs" />
|
||||
<Compile Include="RunnerOptions.cs" />
|
||||
<Compile Include="Resources\Resource.Designer.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Resources\AboutResources.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="..\xunit.runner.xamarin\xunit.runner.xamarin.projitems" Label="Shared" Condition="Exists('..\xunit.runner.xamarin\xunit.runner.xamarin.projitems')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\dialog_disclosure.png" />
|
||||
<AndroidResource Include="Resources\drawable\dialog_expander_ic_minimized.9.png" />
|
||||
<AndroidResource Include="Resources\layout\dialog_achievements.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_boolfieldleft.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_boolfieldright.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_boolfieldsubleft.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_boolfieldsubright.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_button.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_datefield.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_fieldsetlabel.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_floatimage.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_labelfieldbelow.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_labelfieldright.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_onofffieldright.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_panel.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_root.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_selectlist.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_selectlistfield.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_textarea.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_textfieldbelow.xml" />
|
||||
<AndroidResource Include="Resources\layout\dialog_textfieldright.xml" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,134 @@
|
|||
// TestCaseElement.cs: MonoTouch.Dialog element for TestCase
|
||||
//
|
||||
// Authors:
|
||||
// Sebastien Pouliot <sebastien@xamarin.com>
|
||||
//
|
||||
// Copyright 2011-2013 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MonoTouch.Dialog;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Runners;
|
||||
#if XAMCORE_2_0
|
||||
using UIKit;
|
||||
#else
|
||||
using MonoTouch.UIKit;
|
||||
#endif
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
internal class TestCaseElement : TestElement
|
||||
{
|
||||
public MonoTestCase TestCase { get; private set; }
|
||||
|
||||
public TestCaseElement(MonoTestCase testCase, TouchRunner runner)
|
||||
: base(runner)
|
||||
{
|
||||
TestCase = testCase;
|
||||
Caption = testCase.DisplayName;
|
||||
Value = "NotExecuted";
|
||||
Tapped += async delegate
|
||||
{
|
||||
await Run();
|
||||
|
||||
if (TestCase.Result == TestState.Failed)
|
||||
{
|
||||
var root = new RootElement("Results")
|
||||
{
|
||||
new Section()
|
||||
{
|
||||
new TestResultElement(TestCase.TestResult)
|
||||
}
|
||||
};
|
||||
var dvc = new DialogViewController(root, true)
|
||||
{
|
||||
Autorotate = true
|
||||
};
|
||||
Runner.NavigationController.PushViewController(dvc, true);
|
||||
}
|
||||
};
|
||||
|
||||
testCase.TestCaseUpdated += OnTestCaseUpdated;
|
||||
}
|
||||
|
||||
private void OnTestCaseUpdated(object sender, EventArgs e)
|
||||
{
|
||||
UpdateResult();
|
||||
}
|
||||
|
||||
protected override void OptionsChanged()
|
||||
{
|
||||
Caption = TestCase.DisplayName;
|
||||
base.OptionsChanged();
|
||||
|
||||
}
|
||||
|
||||
public override TestState Result
|
||||
{
|
||||
get { return TestCase.Result; }
|
||||
}
|
||||
|
||||
private void UpdateResult()
|
||||
{
|
||||
|
||||
if (TestCase.Result == TestState.Skipped)
|
||||
{
|
||||
Value = ((ITestSkipped)TestCase.TestResult.TestResultMessage).Reason;
|
||||
DetailColor = UIColor.Orange;
|
||||
}
|
||||
else if (TestCase.Result == TestState.Passed)
|
||||
{
|
||||
Value = String.Format("Success! {0} ms", TestCase.TestResult.Duration.TotalMilliseconds);
|
||||
DetailColor = DarkGreen;
|
||||
}
|
||||
else if (TestCase.Result == TestState.Failed)
|
||||
{
|
||||
Value = TestCase.TestResult.ErrorMessage;
|
||||
DetailColor = UIColor.Red;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Assert.Ignore falls into this
|
||||
Value = TestCase.TestResult.ErrorMessage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private async Task Run()
|
||||
{
|
||||
if (TestCase.Result == TestState.NotRun)
|
||||
{
|
||||
await Runner.Run(TestCase);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
TestCase.TestCaseUpdated -= OnTestCaseUpdated;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
// TestElement.cs: MonoTouch.Dialog element for ITest, i.e. TestSuite and TestCase
|
||||
//
|
||||
// Authors:
|
||||
// Sebastien Pouliot <sebastien@xamarin.com>
|
||||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
#if XAMCORE_2_0
|
||||
using UIKit;
|
||||
#else
|
||||
using MonoTouch.UIKit;
|
||||
#endif
|
||||
|
||||
using MonoTouch.Dialog;
|
||||
|
||||
|
||||
namespace Xunit.Runners.UI {
|
||||
|
||||
abstract class TestElement : StyledMultilineElement {
|
||||
|
||||
static internal UIColor DarkGreen = UIColor.FromRGB (0x00, 0x77, 0x00);
|
||||
|
||||
public TestElement ( TouchRunner runner)
|
||||
: base ("?", "?", UITableViewCellStyle.Subtitle)
|
||||
{
|
||||
if (runner == null)
|
||||
throw new ArgumentNullException ("runner");
|
||||
|
||||
Runner = runner;
|
||||
|
||||
// Normally this would be a bad thing, an event on a static class
|
||||
// given the lifespan of these elements, it doesn't matter.
|
||||
RunnerOptions.Current.OptionsChanged +=
|
||||
(sender, args) =>
|
||||
{
|
||||
OptionsChanged();
|
||||
|
||||
Refresh();
|
||||
};
|
||||
}
|
||||
|
||||
protected virtual void OptionsChanged()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected void Refresh()
|
||||
{
|
||||
if (GetContainerTableView() != null)
|
||||
{
|
||||
var root = GetImmediateRootElement();
|
||||
root.Reload(this, UITableViewRowAnimation.None);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract TestState Result { get; }
|
||||
|
||||
protected TouchRunner Runner { get; private set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// TestResultElement.cs: MonoTouch.Dialog element for TestResult
|
||||
//
|
||||
// Authors:
|
||||
// Sebastien Pouliot <sebastien@xamarin.com>
|
||||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MonoTouch.Dialog;
|
||||
using Xunit.Runners;
|
||||
#if XAMCORE_2_0
|
||||
using UIKit;
|
||||
#else
|
||||
using MonoTouch.UIKit;
|
||||
#endif
|
||||
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
internal class TestResultElement : StyledMultilineElement
|
||||
{
|
||||
public TestResultElement(MonoTestResult result) :
|
||||
base(result.ErrorMessage ?? "Unknown error", result.ErrorStackTrace, UITableViewCellStyle.Subtitle)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
// TestSuiteElement.cs: MonoTouch.Dialog element for TestSuite
|
||||
//
|
||||
// Authors:
|
||||
// Sebastien Pouliot <sebastien@xamarin.com>
|
||||
//
|
||||
// Copyright 2011-2013 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Runners;
|
||||
#if XAMCORE_2_0
|
||||
using UIKit;
|
||||
#else
|
||||
using MonoTouch.UIKit;
|
||||
#endif
|
||||
|
||||
using MonoTouch.Dialog;
|
||||
|
||||
|
||||
namespace Xunit.Runners.UI {
|
||||
|
||||
class TestSuiteElement : TestElement {
|
||||
private readonly IEnumerable<TestCaseElement> testCases;
|
||||
private TestState result = TestState.NotRun;
|
||||
|
||||
public TestSuiteElement(string sourceName, IEnumerable<TestCaseElement> testCases, TouchRunner runner)
|
||||
: base (runner)
|
||||
{
|
||||
this.testCases = testCases;
|
||||
Caption = sourceName;
|
||||
int count = testCases.Count();
|
||||
if (count > 0) {
|
||||
Accessory = UITableViewCellAccessory.DisclosureIndicator;
|
||||
DetailColor = DarkGreen;
|
||||
//Value = String.Format ("{0} test case{1}, {2}", count, count == 1 ? String.Empty : "s", Suite.RunState);
|
||||
Value = String.Format ("{0} test case{1}", count, count == 1 ? String.Empty : "s");
|
||||
Tapped += delegate {
|
||||
runner.Show (sourceName);
|
||||
};
|
||||
} else {
|
||||
DetailColor = UIColor.Orange;
|
||||
Value = "No test was found inside this suite";
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var outcomes = testCases.GroupBy(r => r.TestCase.Result);
|
||||
|
||||
var results = outcomes.ToDictionary(k => k.Key, v => v.Count());
|
||||
|
||||
int positive;
|
||||
results.TryGetValue(TestState.Passed, out positive);
|
||||
|
||||
int failure;
|
||||
results.TryGetValue(TestState.Failed, out failure);
|
||||
|
||||
int skipped;
|
||||
results.TryGetValue(TestState.Skipped, out skipped);
|
||||
|
||||
|
||||
var sb = new StringBuilder();
|
||||
if (failure == 0)
|
||||
{
|
||||
DetailColor = DarkGreen;
|
||||
|
||||
var totalTime = testCases.Sum(r => r.TestCase.TestResult.Duration.TotalMilliseconds);
|
||||
|
||||
sb.Append("Success! ").Append(totalTime).Append(" ms for ").Append(positive).Append(" test");
|
||||
if (positive > 1)
|
||||
sb.Append('s');
|
||||
|
||||
result = TestState.Passed;
|
||||
}
|
||||
else
|
||||
{
|
||||
DetailColor = UIColor.Red;
|
||||
if (positive > 0)
|
||||
sb.Append(positive).Append(" success");
|
||||
if (sb.Length > 0)
|
||||
sb.Append(", ");
|
||||
sb.Append(failure).Append(" failure");
|
||||
if (failure > 1)
|
||||
sb.Append('s');
|
||||
if (skipped > 0)
|
||||
sb.Append(", ").Append(skipped).Append(" skipped");
|
||||
|
||||
result = TestState.Failed;
|
||||
}
|
||||
Value = sb.ToString();
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public override TestState Result
|
||||
{
|
||||
get { return result; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using MonoTouch.Foundation;
|
||||
using MonoTouch.UIKit;
|
||||
using Xunit.Runners.UI;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace $rootnamespace$
|
||||
{
|
||||
// The UIApplicationDelegate for the application. This class is responsible for launching the
|
||||
// User Interface of the application, as well as listening (and optionally responding) to
|
||||
// application events from iOS.
|
||||
[Register("AppDelegate")]
|
||||
public partial class AppDelegate : UIApplicationDelegate
|
||||
{
|
||||
// class-level declarations
|
||||
UIWindow window;
|
||||
TouchRunner runner;
|
||||
|
||||
//
|
||||
// This method is invoked when the application has loaded and is ready to run. In this
|
||||
// method you should instantiate the window, load the UI into it and then make the window
|
||||
// visible.
|
||||
//
|
||||
// You have 17 seconds to return from this method, or iOS will terminate your application.
|
||||
//
|
||||
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
|
||||
{
|
||||
// create a new window instance based on the screen size
|
||||
window = new UIWindow(UIScreen.MainScreen.Bounds);
|
||||
runner = new TouchRunner(window);
|
||||
|
||||
// We need this to ensure the execution assembly is part of the app bundle
|
||||
runner.AddExecutionAssembly(typeof(ExtensibilityPointFactory).Assembly);
|
||||
|
||||
// tests can be inside the main assembly
|
||||
runner.Add(Assembly.GetExecutingAssembly());
|
||||
// otherwise you need to ensure that the test assemblies will
|
||||
// become part of the app bundle
|
||||
// runner.Add(typeof(MonoTouchFixtures.Test.Test).Assembly);
|
||||
#if false
|
||||
// you can use the default or set your own custom writer (e.g. save to web site and tweet it ;-)
|
||||
runner.Writer = new TcpTextWriter ("10.0.1.2", 16384);
|
||||
// start running the test suites as soon as the application is loaded
|
||||
runner.AutoStart = true;
|
||||
// crash the application (to ensure it's ended) and return to springboard
|
||||
runner.TerminateAfterExecution = true;
|
||||
#endif
|
||||
|
||||
window.RootViewController = new UINavigationController(runner.GetViewController());
|
||||
|
||||
// make the window visible
|
||||
window.MakeKeyAndVisible();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("xunit.runner.ios")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("xunit.runner.ios")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("30cdbbd4-16b2-4b90-a8d8-d4c55cb6703c")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,173 @@
|
|||
// TouchOptions.cs: MonoTouch.Dialog-based options
|
||||
//
|
||||
// Authors:
|
||||
// Sebastien Pouliot <sebastien@xamarin.com>
|
||||
//
|
||||
// Copyright 2011-2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
using Xunit.Runners;
|
||||
#if XAMCORE_2_0
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
#else
|
||||
using MonoTouch.Foundation;
|
||||
using MonoTouch.UIKit;
|
||||
#endif
|
||||
|
||||
using MonoTouch.Dialog;
|
||||
|
||||
using Mono.Options;
|
||||
|
||||
namespace Xunit.Runners.UI {
|
||||
|
||||
public class RunnerOptions {
|
||||
|
||||
static public readonly RunnerOptions Current = new RunnerOptions ();
|
||||
|
||||
// Normally this would be a bad thing, an event on a static class
|
||||
// given the lifespan of these elements, it doesn't matter.
|
||||
public event EventHandler OptionsChanged;
|
||||
|
||||
public RunnerOptions ()
|
||||
{
|
||||
var defaults = NSUserDefaults.StandardUserDefaults;
|
||||
EnableNetwork = defaults.BoolForKey("network.enabled");
|
||||
HostName = defaults.StringForKey("network.host.name");
|
||||
HostPort = (int)defaults.IntForKey("network.host.port");
|
||||
SortNames = defaults.BoolForKey ("display.sort");
|
||||
ParallelizeAssemblies = defaults.BoolForKey("exec.parallel");
|
||||
var dnKey = defaults.IntForKey("display.NameDisplay");
|
||||
if(dnKey == 0)
|
||||
NameDisplay = NameDisplay.Short;
|
||||
else
|
||||
{
|
||||
NameDisplay = (NameDisplay)dnKey;
|
||||
}
|
||||
|
||||
|
||||
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) },
|
||||
{ "enablenetwork", "Enable the network reporter.", v => EnableNetwork = true },
|
||||
};
|
||||
|
||||
try {
|
||||
os.Parse (Environment.GetCommandLineArgs ());
|
||||
} catch (OptionException oe) {
|
||||
Console.WriteLine ("{0} for options '{1}'", oe.Message, oe.OptionName);
|
||||
}
|
||||
}
|
||||
|
||||
private bool EnableNetwork { get; set; }
|
||||
|
||||
public string HostName { get; private set; }
|
||||
|
||||
public int HostPort { get; private set; }
|
||||
|
||||
public bool AutoStart { get; set; }
|
||||
|
||||
public bool TerminateAfterExecution { get; set; }
|
||||
|
||||
public bool ShowUseNetworkLogger
|
||||
{
|
||||
get { return (EnableNetwork && !String.IsNullOrWhiteSpace(HostName) && (HostPort > 0)); }
|
||||
}
|
||||
|
||||
public bool SortNames { get; set; }
|
||||
public NameDisplay NameDisplay { get; set; }
|
||||
|
||||
public bool ParallelizeAssemblies { get; set; }
|
||||
|
||||
[CLSCompliant (false)]
|
||||
public UIViewController GetViewController ()
|
||||
{
|
||||
var network = new BooleanElement("Enable", EnableNetwork);
|
||||
|
||||
var host = new EntryElement("Host Name", "name", HostName);
|
||||
host.KeyboardType = UIKeyboardType.ASCIICapable;
|
||||
|
||||
var port = new EntryElement("Port", "name", HostPort.ToString());
|
||||
port.KeyboardType = UIKeyboardType.NumberPad;
|
||||
|
||||
var sort = new BooleanElement ("Sort Names", SortNames);
|
||||
var nameDisplayFull = new RadioElement("Full", "nameDisplay");
|
||||
var nameDisplayShort = new RadioElement("Short", "nameDisplay");
|
||||
|
||||
var nameDisplayGroup = new RadioGroup("nameDisplay", NameDisplay == NameDisplay.Short ? 1 : 0);
|
||||
|
||||
var par = new BooleanElement("Parallelize Assemblies", ParallelizeAssemblies);
|
||||
|
||||
var root = new RootElement ("Options") {
|
||||
new Section ("Remote Server") { network, host, port },
|
||||
new Section("Execution") { par },
|
||||
|
||||
new Section ("Display")
|
||||
{
|
||||
sort,
|
||||
new RootElement("Name Display", nameDisplayGroup)
|
||||
{
|
||||
new Section()
|
||||
{
|
||||
nameDisplayFull,
|
||||
nameDisplayShort
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var dv = new DialogViewController (root, true) { Autorotate = true };
|
||||
dv.ViewDisappearing
|
||||
+= delegate
|
||||
{
|
||||
EnableNetwork = network.Value;
|
||||
HostName = host.Value;
|
||||
ushort p;
|
||||
if (UInt16.TryParse(port.Value, out p))
|
||||
HostPort = p;
|
||||
else
|
||||
HostPort = -1;
|
||||
SortNames = sort.Value;
|
||||
ParallelizeAssemblies = par.Value;
|
||||
NameDisplay = nameDisplayGroup.Selected == 1 ? NameDisplay.Short : NameDisplay.Full;
|
||||
|
||||
var defaults = NSUserDefaults.StandardUserDefaults;
|
||||
defaults.SetBool(EnableNetwork, "network.enabled");
|
||||
defaults.SetString(HostName ?? String.Empty, "network.host.name");
|
||||
defaults.SetInt(HostPort, "network.host.port");
|
||||
|
||||
defaults.SetInt((int)NameDisplay, "display.nameDisplay");
|
||||
defaults.SetBool(SortNames, "display.sort");
|
||||
defaults.SetBool(ParallelizeAssemblies, "exec.parallel");
|
||||
|
||||
if (OptionsChanged != null)
|
||||
OptionsChanged(this, EventArgs.Empty);
|
||||
};
|
||||
|
||||
return dv;
|
||||
}
|
||||
public string GetDisplayName(string displayName, string shortMethodName, string fullyQualifiedMethodName)
|
||||
{
|
||||
if (NameDisplay == NameDisplay.Full)
|
||||
return displayName;
|
||||
if (displayName == fullyQualifiedMethodName || displayName.StartsWith(fullyQualifiedMethodName + "("))
|
||||
return shortMethodName + displayName.Substring(fullyQualifiedMethodName.Length);
|
||||
return displayName;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,696 @@
|
|||
// TouchRunner.cs: MonoTouch.Dialog-based driver to run unit tests
|
||||
//
|
||||
// Authors:
|
||||
// Sebastien Pouliot <sebastien@xamarin.com>
|
||||
//
|
||||
// Copyright 2011-2013 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MonoTouch;
|
||||
using MonoTouch.Dialog;
|
||||
using Xunit;
|
||||
using Xunit.Runners;
|
||||
using Xunit.Runners.Utilities;
|
||||
using Xunit.Runners.Visitors;
|
||||
#if XAMCORE_2_0
|
||||
using Foundation;
|
||||
using ObjCRuntime;
|
||||
using UIKit;
|
||||
using Constants = global::ObjCRuntime.Constants;
|
||||
#else
|
||||
using MonoTouch.Foundation;
|
||||
using MonoTouch.ObjCRuntime;
|
||||
using MonoTouch.UIKit;
|
||||
#endif
|
||||
|
||||
|
||||
namespace Xunit.Runners.UI
|
||||
{
|
||||
public class TouchRunner : ITestListener
|
||||
{
|
||||
private readonly List<Assembly> assemblies = new List<Assembly>();
|
||||
|
||||
private readonly ManualResetEvent mre = new ManualResetEvent(false);
|
||||
private readonly Dictionary<string, TestSuiteElement> suite_elements = new Dictionary<string, TestSuiteElement>();
|
||||
private readonly Dictionary<string, TouchViewController> suites_dvc = new Dictionary<string, TouchViewController>();
|
||||
private readonly UIWindow window;
|
||||
private Assembly executionAssembly;
|
||||
private int failed;
|
||||
private int skipped;
|
||||
private int passed;
|
||||
private bool cancelled;
|
||||
private IEnumerable<IGrouping<string, MonoTestCase>> allTests;
|
||||
|
||||
private readonly AsyncLock executionLock = new AsyncLock();
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public TouchRunner(UIWindow window)
|
||||
{
|
||||
if (window == null)
|
||||
throw new ArgumentNullException("window");
|
||||
|
||||
this.window = window;
|
||||
}
|
||||
|
||||
public bool AutoStart
|
||||
{
|
||||
get { return RunnerOptions.Current.AutoStart; }
|
||||
set { RunnerOptions.Current.AutoStart = value; }
|
||||
}
|
||||
|
||||
public bool TerminateAfterExecution
|
||||
{
|
||||
get { return RunnerOptions.Current.TerminateAfterExecution; }
|
||||
set { RunnerOptions.Current.TerminateAfterExecution = value; }
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public UINavigationController NavigationController
|
||||
{
|
||||
get { return (UINavigationController)window.RootViewController; }
|
||||
}
|
||||
|
||||
private void OnTestRunCompleted()
|
||||
{
|
||||
window.BeginInvokeOnMainThread(
|
||||
() =>
|
||||
{
|
||||
foreach(var ts in suite_elements.Values)
|
||||
{
|
||||
// Recalc the status
|
||||
ts.Update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ITestListener.RecordResult(MonoTestResult result)
|
||||
{
|
||||
// Ensure the proeprty changes/updates are on the UI thread
|
||||
window.BeginInvokeOnMainThread(result.RaiseTestUpdated);
|
||||
|
||||
if (result.TestCase.Result == TestState.Passed)
|
||||
{
|
||||
Writer.Write("\t[PASS] ");
|
||||
passed++;
|
||||
}
|
||||
else if (result.TestCase.Result == TestState.Skipped)
|
||||
{
|
||||
Writer.Write("\t[SKIPPED] ");
|
||||
skipped++;
|
||||
}
|
||||
else if (result.TestCase.Result == TestState.Failed)
|
||||
{
|
||||
Writer.Write("\t[FAIL] ");
|
||||
failed++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write("\t[INFO] ");
|
||||
}
|
||||
Writer.Write(result.TestCase.DisplayName);
|
||||
|
||||
var message = result.ErrorMessage;
|
||||
if (!String.IsNullOrEmpty(message))
|
||||
{
|
||||
Writer.Write(" : {0}", message.Replace("\r\n", "\\r\\n"));
|
||||
}
|
||||
Writer.WriteLine();
|
||||
|
||||
var stacktrace = result.ErrorStackTrace;
|
||||
if (!String.IsNullOrEmpty(result.ErrorStackTrace))
|
||||
{
|
||||
var lines = stacktrace.Split(new char[] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var line in lines)
|
||||
Writer.WriteLine("\t\t{0}", line);
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(Assembly assembly)
|
||||
{
|
||||
if (assembly == null)
|
||||
throw new ArgumentNullException("assembly");
|
||||
|
||||
assemblies.Add(assembly);
|
||||
}
|
||||
|
||||
// This is here due to iOS AOT. We need the assm to be in the app dir
|
||||
public void AddExecutionAssembly(Assembly assembly)
|
||||
{
|
||||
if (assembly == null) throw new ArgumentNullException("assembly");
|
||||
executionAssembly = assembly;
|
||||
}
|
||||
|
||||
private IEnumerable<IGrouping<string, MonoTestCase>> DiscoverTestsInAssemblies()
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
var result = new List<IGrouping<string, MonoTestCase>>();
|
||||
|
||||
try
|
||||
{
|
||||
using (AssemblyHelper.SubscribeResolve())
|
||||
{
|
||||
foreach (var assm in assemblies)
|
||||
{
|
||||
// Xunit needs the file name
|
||||
var fileName = Path.GetFileName(assm.Location);
|
||||
|
||||
try
|
||||
{
|
||||
using (var framework = new XunitFrontController(fileName, configFileName: null, shadowCopy: true))
|
||||
using (var sink = new TestDiscoveryVisitor())
|
||||
{
|
||||
framework.Find(includeSourceInformation: true, messageSink: sink, options: new TestFrameworkOptions());
|
||||
sink.Finished.WaitOne();
|
||||
|
||||
result.Add(
|
||||
new Grouping<string, MonoTestCase>(
|
||||
fileName,
|
||||
sink.TestCases
|
||||
.GroupBy(tc => String.Format("{0}.{1}", tc.Class.Name, tc.Method.Name))
|
||||
.SelectMany(group =>
|
||||
group.Select(testCase =>
|
||||
new MonoTestCase(fileName, testCase, forceUniqueNames: group.Count() > 1)))
|
||||
.ToList()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e);
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static void TerminateWithSuccess()
|
||||
{
|
||||
var selector = new Selector("terminateWithSuccess");
|
||||
UIApplication.SharedApplication.PerformSelector(selector, UIApplication.SharedApplication, 0);
|
||||
}
|
||||
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public UIViewController GetViewController()
|
||||
{
|
||||
var menu = new RootElement("Test Runner");
|
||||
|
||||
var main = new Section("Loading test suites...");
|
||||
menu.Add(main);
|
||||
|
||||
var options = new Section()
|
||||
{
|
||||
new StyledStringElement("Options", Options)
|
||||
{
|
||||
Accessory = UITableViewCellAccessory.DisclosureIndicator
|
||||
},
|
||||
new StyledStringElement("Credits", Credits)
|
||||
{
|
||||
Accessory = UITableViewCellAccessory.DisclosureIndicator
|
||||
}
|
||||
};
|
||||
menu.Add(options);
|
||||
|
||||
// large unit tests applications can take more time to initialize
|
||||
// than what the iOS watchdog will allow them on devices
|
||||
ThreadPool.QueueUserWorkItem(delegate
|
||||
{
|
||||
allTests = DiscoverTestsInAssemblies();
|
||||
|
||||
|
||||
window.InvokeOnMainThread(
|
||||
delegate
|
||||
{
|
||||
foreach (var group in allTests)
|
||||
{
|
||||
main.Add(SetupSource(group));
|
||||
}
|
||||
|
||||
|
||||
mre.Set();
|
||||
|
||||
main.Caption = null;
|
||||
menu.Reload(main, UITableViewRowAnimation.Fade);
|
||||
|
||||
options.Insert(0, new StringElement("Run Everything", async () => await Run()));
|
||||
menu.Reload(options, UITableViewRowAnimation.Fade);
|
||||
});
|
||||
assemblies.Clear();
|
||||
});
|
||||
|
||||
var dv = new DialogViewController(menu)
|
||||
{
|
||||
Autorotate = true
|
||||
};
|
||||
|
||||
// AutoStart running the tests (with either the supplied 'writer' or the options)
|
||||
if (AutoStart)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(delegate
|
||||
{
|
||||
mre.WaitOne();
|
||||
window.BeginInvokeOnMainThread(async delegate
|
||||
{
|
||||
await Run();
|
||||
// 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();
|
||||
});
|
||||
});
|
||||
}
|
||||
return dv;
|
||||
}
|
||||
|
||||
private Task Run()
|
||||
{
|
||||
return Run(allTests.SelectMany(t => t), "Run Everything");
|
||||
}
|
||||
|
||||
private void Options()
|
||||
{
|
||||
NavigationController.PushViewController(RunnerOptions.Current.GetViewController(), true);
|
||||
}
|
||||
|
||||
private void Credits()
|
||||
{
|
||||
var title = new MultilineElement("xUnit MonoTouch Runner\nCopyright 2014 Outercurve Foundation\nAll rights reserved.");
|
||||
title.Alignment = UITextAlignment.Center;
|
||||
|
||||
var root = new RootElement("Credits")
|
||||
{
|
||||
new Section()
|
||||
{
|
||||
title
|
||||
},
|
||||
new Section()
|
||||
{
|
||||
new HtmlElement("About Xamarin", "http://www.xamarin.com"),
|
||||
new HtmlElement("About MonoTouch", "http://ios.xamarin.com"),
|
||||
new HtmlElement("About MonoTouch.Dialog", "https://github.com/migueldeicaza/MonoTouch.Dialog"),
|
||||
new HtmlElement("About xUnit", "https://github.com/xunit/xunit"),
|
||||
new HtmlElement("About Font Awesome", "http://fortawesome.github.com/Font-Awesome")
|
||||
}
|
||||
};
|
||||
|
||||
var dv = new DialogViewController(root, true)
|
||||
{
|
||||
Autorotate = true
|
||||
};
|
||||
NavigationController.PushViewController(dv, true);
|
||||
}
|
||||
|
||||
public void Show(string suite)
|
||||
{
|
||||
NavigationController.PushViewController(suites_dvc[suite], true);
|
||||
}
|
||||
|
||||
private TestSuiteElement SetupSource(IGrouping<string, MonoTestCase> testSource)
|
||||
{
|
||||
|
||||
var root = new RootElement("Tests");
|
||||
|
||||
var elements = new List<TestCaseElement>();
|
||||
|
||||
var section = new Section(testSource.Key);
|
||||
foreach (var test in testSource)
|
||||
{
|
||||
var ele = new TestCaseElement(test, this);
|
||||
elements.Add(ele);
|
||||
section.Add(ele);
|
||||
}
|
||||
|
||||
var tse = new TestSuiteElement(testSource.Key, elements, this);
|
||||
suite_elements.Add(testSource.Key, tse);
|
||||
|
||||
|
||||
root.Add(section);
|
||||
|
||||
if (section.Count > 1)
|
||||
{
|
||||
StringElement allbtn = null;
|
||||
allbtn = new StringElement("Run all",
|
||||
async delegate
|
||||
{
|
||||
|
||||
//var table = allbtn.GetContainerTableView();
|
||||
//var cell = allbtn.GetCell(table);
|
||||
//cell.UserInteractionEnabled = false;
|
||||
//cell.SelectionStyle = UITableViewCellSelectionStyle.None;
|
||||
//cell.
|
||||
await Run(testSource);
|
||||
|
||||
//cell.UserInteractionEnabled = true;
|
||||
//cell.SelectionStyle = UITableViewCellSelectionStyle.Default;
|
||||
|
||||
suites_dvc[testSource.Key].Filter();
|
||||
});
|
||||
var options = new Section()
|
||||
{
|
||||
allbtn
|
||||
};
|
||||
|
||||
root.Add(options);
|
||||
}
|
||||
|
||||
suites_dvc.Add(testSource.Key, new TouchViewController(root));
|
||||
return tse;
|
||||
}
|
||||
|
||||
Task RunTests(IEnumerable<IGrouping<string, MonoTestCase>> testCaseAccessor, Stopwatch stopwatch)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>(null);
|
||||
|
||||
ThreadPool.QueueUserWorkItem(state =>
|
||||
{
|
||||
var toDispose = new List<IDisposable>();
|
||||
|
||||
try
|
||||
{
|
||||
cancelled = false;
|
||||
|
||||
using (AssemblyHelper.SubscribeResolve())
|
||||
if (RunnerOptions.Current.ParallelizeAssemblies)
|
||||
testCaseAccessor
|
||||
.Select(testCaseGroup => RunTestsInAssemblyAsync(toDispose, testCaseGroup.Key, testCaseGroup, stopwatch))
|
||||
.ToList()
|
||||
.ForEach(@event => @event.WaitOne());
|
||||
else
|
||||
testCaseAccessor
|
||||
.ToList()
|
||||
.ForEach(testCaseGroup => RunTestsInAssembly(toDispose, testCaseGroup.Key, testCaseGroup, stopwatch));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
tcs.SetException(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
toDispose.ForEach(disposable => disposable.Dispose());
|
||||
OnTestRunCompleted();
|
||||
tcs.SetResult(null);
|
||||
}
|
||||
});
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
ManualResetEvent RunTestsInAssemblyAsync(List<IDisposable> toDispose,
|
||||
string assemblyFileName,
|
||||
IEnumerable<MonoTestCase> testCases,
|
||||
Stopwatch stopwatch)
|
||||
{
|
||||
var @event = new ManualResetEvent(initialState: false);
|
||||
|
||||
ThreadPool.QueueUserWorkItem(_ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
RunTestsInAssembly(toDispose, assemblyFileName, testCases, stopwatch);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@event.Set();
|
||||
}
|
||||
});
|
||||
|
||||
return @event;
|
||||
}
|
||||
|
||||
void RunTestsInAssembly(List<IDisposable> toDispose,
|
||||
string assemblyFileName,
|
||||
IEnumerable<MonoTestCase> testCases,
|
||||
Stopwatch stopwatch)
|
||||
{
|
||||
if (cancelled)
|
||||
return;
|
||||
|
||||
var controller = new XunitFrontController(assemblyFileName, configFileName: null, shadowCopy: true);
|
||||
|
||||
lock (toDispose)
|
||||
toDispose.Add(controller);
|
||||
|
||||
var xunitTestCases = testCases.ToDictionary(tc => tc.TestCase);
|
||||
|
||||
using (var executionVisitor = new MonoTestExecutionVisitor(xunitTestCases, this, () => cancelled))
|
||||
{
|
||||
var executionOptions = new XunitExecutionOptions
|
||||
{
|
||||
//DisableParallelization = !settings.ParallelizeTestCollections,
|
||||
//MaxParallelThreads = settings.MaxParallelThreads
|
||||
};
|
||||
|
||||
controller.RunTests(xunitTestCases.Keys.ToList(), executionVisitor, executionOptions);
|
||||
executionVisitor.Finished.WaitOne();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//public void TestStarted (ITestCase test)
|
||||
//{
|
||||
// if (test is TestSuite) {
|
||||
// Writer.WriteLine ();
|
||||
// Writer.WriteLine (test.Name);
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
internal Task Run(MonoTestCase test)
|
||||
{
|
||||
return Run(new[] { test });
|
||||
}
|
||||
|
||||
internal async Task Run(IEnumerable<MonoTestCase> tests, string message = null)
|
||||
{
|
||||
|
||||
var stopWatch = Stopwatch.StartNew();
|
||||
|
||||
var groups = tests.GroupBy(t => t.AssemblyFileName);
|
||||
using (await executionLock.LockAsync())
|
||||
{
|
||||
if(message == null)
|
||||
message = tests.Count() > 1 ? "Run Multiple Tests" : tests.First()
|
||||
.DisplayName;
|
||||
|
||||
if (!OpenWriter(message))
|
||||
return;
|
||||
try
|
||||
{
|
||||
await RunTests(groups, stopWatch);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseWriter();
|
||||
}
|
||||
}
|
||||
|
||||
stopWatch.Stop();
|
||||
}
|
||||
|
||||
#region writer
|
||||
|
||||
public TextWriter Writer { get; set; }
|
||||
|
||||
private static string UniqueIdentifier
|
||||
{
|
||||
get
|
||||
{
|
||||
var handle = UIDevice.CurrentDevice.Handle;
|
||||
if (UIDevice.CurrentDevice.RespondsToSelector(new Selector("uniqueIdentifier")))
|
||||
return NSString.FromHandle(objc_msgSend(handle, Selector.GetHandle("uniqueIdentifier")));
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
private static string SelectHostName(string[] names, int port)
|
||||
{
|
||||
if (names.Length == 0)
|
||||
return null;
|
||||
|
||||
if (names.Length == 1)
|
||||
return names[0];
|
||||
|
||||
var lock_obj = new object();
|
||||
string result = null;
|
||||
var failures = 0;
|
||||
|
||||
using (var evt = new ManualResetEvent(false))
|
||||
{
|
||||
for (var i = names.Length - 1; i >= 0; i--)
|
||||
{
|
||||
var name = names[i];
|
||||
ThreadPool.QueueUserWorkItem((v) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = new TcpClient(name, port);
|
||||
using (var writer = new StreamWriter(client.GetStream()))
|
||||
{
|
||||
writer.WriteLine("ping");
|
||||
}
|
||||
lock (lock_obj)
|
||||
{
|
||||
if (result == null)
|
||||
result = name;
|
||||
}
|
||||
evt.Set();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
lock (lock_obj)
|
||||
{
|
||||
failures++;
|
||||
if (failures == names.Length)
|
||||
evt.Set();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Wait for 1 success or all failures
|
||||
evt.WaitOne();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool OpenWriter(string message)
|
||||
{
|
||||
RunnerOptions options = RunnerOptions.Current;
|
||||
DateTime now = DateTime.Now;
|
||||
// let the application provide it's own TextWriter to ease automation with AutoStart property
|
||||
if (Writer == null)
|
||||
{
|
||||
if (options.ShowUseNetworkLogger)
|
||||
{
|
||||
var hostname = SelectHostName(options.HostName.Split(','), options.HostPort);
|
||||
|
||||
if (hostname != null)
|
||||
{
|
||||
Console.WriteLine("[{0}] Sending '{1}' results to {2}:{3}", now, message, hostname, options.HostPort);
|
||||
try
|
||||
{
|
||||
Writer = new TcpTextWriter(hostname, options.HostPort);
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
UIAlertView alert = new UIAlertView("Network Error",
|
||||
String.Format("Cannot connect to {0}:{1}. Continue on console ?", hostname, options.HostPort),
|
||||
null, "Cancel", "Continue");
|
||||
int button = -1;
|
||||
alert.Clicked += delegate(object sender, UIButtonEventArgs e)
|
||||
{
|
||||
button = (int)e.ButtonIndex;
|
||||
};
|
||||
alert.Show();
|
||||
while (button == -1)
|
||||
NSRunLoop.Current.RunUntil(NSDate.FromTimeIntervalSinceNow(0.5));
|
||||
Console.WriteLine(button);
|
||||
Console.WriteLine("[Host unreachable: {0}]", button == 0 ? "Execution cancelled" : "Switching to console output");
|
||||
if (button == 0)
|
||||
return false;
|
||||
else
|
||||
Writer = Console.Out;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer = Console.Out;
|
||||
}
|
||||
}
|
||||
|
||||
Writer.WriteLine("[Runner executing:\t{0}]", message);
|
||||
Writer.WriteLine("[MonoTouch Version:\t{0}]", Constants.Version);
|
||||
Writer.WriteLine("[GC:\t{0}{1}]", GC.MaxGeneration == 0 ? "Boehm" : "sgen",
|
||||
NSObject.IsNewRefcountEnabled() ? "+NewRefCount" : String.Empty);
|
||||
UIDevice device = UIDevice.CurrentDevice;
|
||||
Writer.WriteLine("[{0}:\t{1} v{2}]", device.Model, device.SystemName, device.SystemVersion);
|
||||
Writer.WriteLine("[Device Name:\t{0}]", device.Name);
|
||||
Writer.WriteLine("[Device UDID:\t{0}]", UniqueIdentifier);
|
||||
Writer.WriteLine("[Device Locale:\t{0}]", NSLocale.CurrentLocale.Identifier);
|
||||
Writer.WriteLine("[Device Date/Time:\t{0}]", now); // to match earlier C.WL output
|
||||
|
||||
Writer.WriteLine("[Bundle:\t{0}]", NSBundle.MainBundle.BundleIdentifier);
|
||||
// FIXME: add data about how the app was compiled (e.g. ARMvX, LLVM, GC and Linker options)
|
||||
passed = 0;
|
||||
skipped = 0;
|
||||
failed = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
[DllImport("/usr/lib/libobjc.dylib")]
|
||||
private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector);
|
||||
|
||||
// Apple blacklisted `uniqueIdentifier` (for the appstore) but it's still
|
||||
// something useful to have inside the test logs
|
||||
|
||||
public void CloseWriter()
|
||||
{
|
||||
var total = passed + failed; // ignored are *not* run
|
||||
Writer.WriteLine("Tests run: {0} Passed: {1} Failed: {2} Skipped: {3}", total, passed, failed, skipped);
|
||||
|
||||
Writer.Close();
|
||||
Writer = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private class Grouping<TKey, TElement> : IGrouping<TKey, TElement>
|
||||
{
|
||||
private readonly IEnumerable<TElement> elements;
|
||||
|
||||
public Grouping(TKey key, IEnumerable<TElement> elements)
|
||||
{
|
||||
Key = key;
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
public TKey Key { get; private set; }
|
||||
|
||||
public IEnumerator<TElement> GetEnumerator()
|
||||
{
|
||||
return elements.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return elements.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,571 @@
|
|||
// TouchViewController.cs
|
||||
//
|
||||
// Authors:
|
||||
// Sebastien Pouliot <sebastien@xamarin.com>
|
||||
//
|
||||
// Copyright 2012 Xamarin Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using Xunit.Runners;
|
||||
#if XAMCORE_2_0
|
||||
using CoreGraphics;
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
#else
|
||||
using MonoTouch.CoreGraphics;
|
||||
using MonoTouch.Foundation;
|
||||
using MonoTouch.UIKit;
|
||||
#endif
|
||||
|
||||
using MonoTouch.Dialog;
|
||||
|
||||
#if !HAVE_NATIVE_TYPES
|
||||
using CGSize = global::System.Drawing.SizeF;
|
||||
#endif
|
||||
|
||||
namespace Xunit.Runners.UI {
|
||||
|
||||
[CLSCompliant (false)]
|
||||
public partial class TouchViewController : DialogViewController {
|
||||
|
||||
public TouchViewController (RootElement root) : base (root, true)
|
||||
{
|
||||
Autorotate = true;
|
||||
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion (5, 0)) {
|
||||
NavigationItem.RightBarButtonItems = new UIBarButtonItem [] {
|
||||
new UIBarButtonItem (ArrowDown, UIBarButtonItemStyle.Plain, ChangeSort),
|
||||
new UIBarButtonItem (Asterisk, UIBarButtonItemStyle.Plain, ChangeFilter)
|
||||
};
|
||||
}
|
||||
|
||||
Section testcases = root [0];
|
||||
OriginalCaption = testcases.Caption;
|
||||
Unfiltered = new List<Element> ();
|
||||
foreach (Element e in testcases)
|
||||
Unfiltered.Add (e);
|
||||
|
||||
CurrentFilter = ResultFilter.All;
|
||||
Filter ();
|
||||
CurrentSortOrder = SortOrder.None;
|
||||
if (RunnerOptions.Current.SortNames)
|
||||
ChangeSort (this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
// Filter
|
||||
|
||||
UIBarButtonItem FilterButton {
|
||||
get {
|
||||
return NavigationItem.RightBarButtonItems [1];
|
||||
}
|
||||
}
|
||||
|
||||
enum ResultFilter {
|
||||
All,
|
||||
Failed,
|
||||
Ignored,
|
||||
Success,
|
||||
}
|
||||
|
||||
string OriginalCaption { get; set; }
|
||||
List<Element> Unfiltered { get; set; }
|
||||
|
||||
ResultFilter CurrentFilter { get; set; }
|
||||
|
||||
void ChangeFilter (object sender, EventArgs e)
|
||||
{
|
||||
switch (CurrentFilter) {
|
||||
case ResultFilter.All:
|
||||
CurrentFilter = ResultFilter.Failed;
|
||||
FilterButton.Image = RemoveSign;
|
||||
break;
|
||||
case ResultFilter.Failed:
|
||||
CurrentFilter = ResultFilter.Ignored;
|
||||
FilterButton.Image = QuestionSign;
|
||||
break;
|
||||
case ResultFilter.Ignored:
|
||||
CurrentFilter = ResultFilter.Success;
|
||||
FilterButton.Image = OkSign;
|
||||
break;
|
||||
case ResultFilter.Success:
|
||||
CurrentFilter = ResultFilter.All;
|
||||
FilterButton.Image = Asterisk;
|
||||
break;
|
||||
}
|
||||
Filter ();
|
||||
}
|
||||
|
||||
public void Filter ()
|
||||
{
|
||||
Section filtered = new Section ();
|
||||
foreach (TestElement te in Unfiltered) {
|
||||
bool add_element = false;
|
||||
switch (CurrentFilter) {
|
||||
case ResultFilter.All:
|
||||
add_element = true;
|
||||
break;
|
||||
case ResultFilter.Failed:
|
||||
add_element = te.Result == TestState.Failed;
|
||||
break;
|
||||
case ResultFilter.Ignored:
|
||||
add_element = te.Result == TestState.Skipped;
|
||||
break;
|
||||
case ResultFilter.Success:
|
||||
add_element = te.Result == TestState.Passed;
|
||||
break;
|
||||
}
|
||||
|
||||
if (add_element)
|
||||
filtered.Add (te);
|
||||
}
|
||||
Root.RemoveAt (0);
|
||||
if (CurrentFilter == ResultFilter.All) {
|
||||
filtered.Caption = String.Format ("{0} ({1})", OriginalCaption, Unfiltered.Count);
|
||||
} else {
|
||||
filtered.Caption = String.Format ("{0} ({1} : {2}/{3})", OriginalCaption, CurrentFilter, filtered.Count, Unfiltered.Count);
|
||||
}
|
||||
Root.Insert (0, filtered);
|
||||
ReloadData ();
|
||||
}
|
||||
|
||||
// Sort
|
||||
|
||||
UIBarButtonItem SortButton {
|
||||
get {
|
||||
return NavigationItem.RightBarButtonItems [0];
|
||||
}
|
||||
}
|
||||
|
||||
enum SortOrder {
|
||||
None,
|
||||
Ascending,
|
||||
Descending
|
||||
}
|
||||
|
||||
SortOrder CurrentSortOrder { get; set; }
|
||||
|
||||
static ElementComparer Ascending = new ElementComparer (SortOrder.Ascending);
|
||||
static ElementComparer Descending = new ElementComparer (SortOrder.Descending);
|
||||
|
||||
void ChangeSort (object sender, EventArgs e)
|
||||
{
|
||||
List<Element> list = Root [0].Elements;
|
||||
switch (CurrentSortOrder) {
|
||||
case SortOrder.Ascending:
|
||||
SortButton.Image = ArrowUp;
|
||||
CurrentSortOrder = SortOrder.Descending;
|
||||
list.Sort (Descending);
|
||||
break;
|
||||
default:
|
||||
SortButton.Image = ArrowDown;
|
||||
CurrentSortOrder = SortOrder.Ascending;
|
||||
list.Sort (Ascending);
|
||||
break;
|
||||
}
|
||||
ReloadData ();
|
||||
}
|
||||
|
||||
class ElementComparer : IComparer <Element> {
|
||||
int order;
|
||||
|
||||
public ElementComparer (SortOrder sortOrder)
|
||||
{
|
||||
order = sortOrder == SortOrder.Descending ? -1 : 1;
|
||||
}
|
||||
|
||||
public int Compare (Element x, Element y)
|
||||
{
|
||||
return order * x.Caption.CompareTo (y.Caption);
|
||||
}
|
||||
}
|
||||
|
||||
// UI
|
||||
|
||||
static UIImage arrow_up;
|
||||
static UIImage arrow_down;
|
||||
static UIImage ok_sign;
|
||||
static UIImage remove_sign;
|
||||
static UIImage question_sign;
|
||||
static UIImage asterisk;
|
||||
|
||||
static UIImage ArrowUp {
|
||||
get {
|
||||
if (arrow_up == null)
|
||||
arrow_up = GetAwesomeIcon (icon_arrow_up);
|
||||
return arrow_up;
|
||||
}
|
||||
}
|
||||
|
||||
static UIImage ArrowDown {
|
||||
get {
|
||||
if (arrow_down == null)
|
||||
arrow_down = GetAwesomeIcon (icon_arrow_down);
|
||||
return arrow_down;
|
||||
}
|
||||
}
|
||||
|
||||
static UIImage OkSign {
|
||||
get {
|
||||
if (ok_sign == null)
|
||||
ok_sign = GetAwesomeIcon (icon_ok_sign);
|
||||
return ok_sign;
|
||||
}
|
||||
}
|
||||
|
||||
static UIImage RemoveSign {
|
||||
get {
|
||||
if (remove_sign == null)
|
||||
remove_sign = GetAwesomeIcon (icon_remove_sign);
|
||||
return remove_sign;
|
||||
}
|
||||
}
|
||||
|
||||
static UIImage QuestionSign {
|
||||
get {
|
||||
if (question_sign == null)
|
||||
question_sign = GetAwesomeIcon (icon_question_sign);
|
||||
return question_sign;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static UIImage Asterisk {
|
||||
get {
|
||||
if (asterisk == null)
|
||||
asterisk = GetAwesomeIcon (icon_asterisk);
|
||||
return asterisk;
|
||||
}
|
||||
}
|
||||
|
||||
static UIImage GetAwesomeIcon (Action<CGContext> render)
|
||||
{
|
||||
// 20x20 normal, 40x40 retina
|
||||
// https://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/MobileHIG/IconsImages/IconsImages.html
|
||||
// http://tirania.org/blog/archive/2010/Jul-20-2.html
|
||||
float size = 20f;
|
||||
UIGraphics.BeginImageContextWithOptions (new CGSize (size, size), false, 0);
|
||||
using (var c = UIGraphics.GetCurrentContext ()) {
|
||||
c.SetFillColor (1.0f, 1.0f, 1.0f, 1.0f);
|
||||
c.SetStrokeColor (1.0f, 1.0f, 1.0f, 1.0f);
|
||||
c.TranslateCTM (3f, size - 3f);
|
||||
c.ScaleCTM (size / 1000, -size / 1000);
|
||||
render (c);
|
||||
}
|
||||
UIImage img = UIGraphics.GetImageFromCurrentImageContext ();
|
||||
UIGraphics.EndImageContext ();
|
||||
return img;
|
||||
}
|
||||
|
||||
#region generated code
|
||||
|
||||
static void icon_arrow_up (CGContext c)
|
||||
{
|
||||
c.MoveTo (-0.5f, 375f);
|
||||
c.AddQuadCurveToPoint (-1f, 394f, 13f, 408f);
|
||||
c.AddLineToPoint (342f, 736f);
|
||||
c.AddQuadCurveToPoint (356f, 750f, 375f, 750f);
|
||||
c.AddQuadCurveToPoint (394f, 750f, 408f, 736f);
|
||||
c.AddLineToPoint (736f, 408f);
|
||||
c.AddQuadCurveToPoint (750f, 394f, 750f, 375f);
|
||||
c.AddQuadCurveToPoint (750f, 356f, 736f, 342f);
|
||||
c.AddLineToPoint (687f, 293f);
|
||||
c.AddQuadCurveToPoint (673f, 279f, 654.5f, 279f);
|
||||
c.AddQuadCurveToPoint (636f, 279f, 622f, 293f);
|
||||
c.AddLineToPoint (456f, 458f);
|
||||
c.AddLineToPoint (456f, 46f);
|
||||
c.AddQuadCurveToPoint (456f, 27f, 442.5f, 13.5f);
|
||||
c.AddQuadCurveToPoint (429f, 0f, 410f, 0f);
|
||||
c.AddLineToPoint (340f, 0f);
|
||||
c.AddQuadCurveToPoint (320f, 0f, 307f, 13.5f);
|
||||
c.AddQuadCurveToPoint (294f, 27f, 294f, 46f);
|
||||
c.AddLineToPoint (294f, 458f);
|
||||
c.AddLineToPoint (129f, 293f);
|
||||
c.AddQuadCurveToPoint (115f, 279f, 96f, 279f);
|
||||
c.AddQuadCurveToPoint (77f, 279f, 63f, 293f);
|
||||
c.AddLineToPoint (14f, 342f);
|
||||
c.AddQuadCurveToPoint (0f, 356f, -0.5f, 375f);
|
||||
c.ClosePath ();
|
||||
c.MoveTo (-0.5f, 375f);
|
||||
c.FillPath ();
|
||||
c.StrokePath ();
|
||||
}
|
||||
|
||||
static void icon_arrow_down (CGContext c)
|
||||
{
|
||||
c.MoveTo (0f, 374f);
|
||||
c.AddQuadCurveToPoint (0f, 393f, 14f, 407f);
|
||||
c.AddLineToPoint (63f, 456f);
|
||||
c.AddQuadCurveToPoint (77f, 470f, 96f, 470f);
|
||||
c.AddQuadCurveToPoint (115f, 470f, 129f, 456f);
|
||||
c.AddLineToPoint (294f, 291f);
|
||||
c.AddLineToPoint (294f, 703f);
|
||||
c.AddQuadCurveToPoint (294f, 722f, 307.5f, 735.5f);
|
||||
c.AddQuadCurveToPoint (321f, 749f, 340f, 749f);
|
||||
c.AddLineToPoint (410f, 749f);
|
||||
c.AddQuadCurveToPoint (430f, 749f, 443f, 735.5f);
|
||||
c.AddQuadCurveToPoint (456f, 722f, 456f, 703f);
|
||||
c.AddLineToPoint (456f, 291f);
|
||||
c.AddLineToPoint (622f, 456f);
|
||||
c.AddQuadCurveToPoint (636f, 470f, 654.5f, 470f);
|
||||
c.AddQuadCurveToPoint (673f, 470f, 687f, 456f);
|
||||
c.AddLineToPoint (737f, 407f);
|
||||
c.AddQuadCurveToPoint (751f, 393f, 751f, 374f);
|
||||
c.AddQuadCurveToPoint (751f, 355f, 737f, 341f);
|
||||
c.AddLineToPoint (408f, 13f);
|
||||
c.AddQuadCurveToPoint (394f, -1f, 375f, -1f);
|
||||
c.AddQuadCurveToPoint (356f, -1f, 342f, 13f);
|
||||
c.AddLineToPoint (14f, 341f);
|
||||
c.AddQuadCurveToPoint (0f, 355f, 0f, 374f);
|
||||
c.ClosePath ();
|
||||
c.MoveTo (0f, 374f);
|
||||
c.FillPath ();
|
||||
c.StrokePath ();
|
||||
}
|
||||
|
||||
static void icon_remove_sign (CGContext c)
|
||||
{
|
||||
c.MoveTo (0f, 376f);
|
||||
c.AddQuadCurveToPoint (0f, 448f, 27.5f, 517f);
|
||||
c.AddQuadCurveToPoint (55f, 586f, 110f, 641f);
|
||||
c.AddQuadCurveToPoint (165f, 696f, 234f, 723f);
|
||||
c.AddQuadCurveToPoint (303f, 750f, 375f, 750f);
|
||||
c.AddQuadCurveToPoint (447f, 750f, 516f, 723f);
|
||||
c.AddQuadCurveToPoint (585f, 696f, 640f, 641f);
|
||||
c.AddQuadCurveToPoint (695f, 586f, 722.5f, 517f);
|
||||
c.AddQuadCurveToPoint (750f, 448f, 750f, 376f);
|
||||
c.AddQuadCurveToPoint (750f, 304f, 722.5f, 235f);
|
||||
c.AddQuadCurveToPoint (695f, 166f, 640f, 111f);
|
||||
c.AddQuadCurveToPoint (585f, 56f, 516f, 28.5f);
|
||||
c.AddQuadCurveToPoint (447f, 1f, 375f, 1f);
|
||||
c.AddQuadCurveToPoint (303f, 1f, 234f, 28.5f);
|
||||
c.AddQuadCurveToPoint (165f, 56f, 110f, 111f);
|
||||
c.AddQuadCurveToPoint (55f, 166f, 27.5f, 235f);
|
||||
c.AddQuadCurveToPoint (0f, 304f, 0f, 376f);
|
||||
c.ClosePath ();
|
||||
c.MoveTo (0f, 376f);
|
||||
c.MoveTo (185f, 240f);
|
||||
c.AddLineToPoint (240f, 186f);
|
||||
c.AddQuadCurveToPoint (245f, 181f, 251f, 181f);
|
||||
c.AddQuadCurveToPoint (257f, 181f, 262f, 186f);
|
||||
c.AddLineToPoint (376f, 300f);
|
||||
c.AddLineToPoint (479f, 196f);
|
||||
c.AddQuadCurveToPoint (484f, 191f, 490f, 191f);
|
||||
c.AddQuadCurveToPoint (496f, 191f, 501f, 196f);
|
||||
c.AddLineToPoint (554f, 249f);
|
||||
c.AddQuadCurveToPoint (565f, 260f, 554f, 271f);
|
||||
c.AddLineToPoint (450f, 374f);
|
||||
c.AddLineToPoint (565f, 489f);
|
||||
c.AddQuadCurveToPoint (576f, 500f, 565f, 511f);
|
||||
c.AddLineToPoint (510f, 566f);
|
||||
c.AddQuadCurveToPoint (499f, 577f, 488f, 566f);
|
||||
c.AddLineToPoint (374f, 451f);
|
||||
c.AddLineToPoint (270f, 555f);
|
||||
c.AddQuadCurveToPoint (259f, 566f, 248f, 555f);
|
||||
c.AddLineToPoint (196f, 502f);
|
||||
c.AddQuadCurveToPoint (191f, 497f, 191f, 491f);
|
||||
c.AddQuadCurveToPoint (191f, 485f, 196f, 480f);
|
||||
c.AddLineToPoint (299f, 377f);
|
||||
c.AddLineToPoint (185f, 262f);
|
||||
c.AddQuadCurveToPoint (175f, 252f, 185f, 240f);
|
||||
c.ClosePath ();
|
||||
c.MoveTo (185f, 240f);
|
||||
c.FillPath ();
|
||||
c.StrokePath ();
|
||||
}
|
||||
|
||||
static void icon_ok_sign (CGContext c)
|
||||
{
|
||||
c.MoveTo (0f, 375f);
|
||||
c.AddQuadCurveToPoint (0f, 453f, 29.5f, 521f);
|
||||
c.AddQuadCurveToPoint (59f, 589f, 110f, 640f);
|
||||
c.AddQuadCurveToPoint (161f, 691f, 229f, 720.5f);
|
||||
c.AddQuadCurveToPoint (297f, 750f, 375f, 750f);
|
||||
c.AddQuadCurveToPoint (453f, 750f, 521f, 720.5f);
|
||||
c.AddQuadCurveToPoint (589f, 691f, 640f, 640f);
|
||||
c.AddQuadCurveToPoint (691f, 589f, 720.5f, 521f);
|
||||
c.AddQuadCurveToPoint (750f, 453f, 750f, 375f);
|
||||
c.AddQuadCurveToPoint (750f, 297f, 720.5f, 229f);
|
||||
c.AddQuadCurveToPoint (691f, 161f, 640f, 110f);
|
||||
c.AddQuadCurveToPoint (589f, 59f, 521f, 29.5f);
|
||||
c.AddQuadCurveToPoint (453f, 0f, 375f, 0f);
|
||||
c.AddQuadCurveToPoint (297f, 0f, 229f, 29.5f);
|
||||
c.AddQuadCurveToPoint (161f, 59f, 110f, 110f);
|
||||
c.AddQuadCurveToPoint (59f, 161f, 29.5f, 229f);
|
||||
c.AddQuadCurveToPoint (0f, 297f, 0f, 375f);
|
||||
c.ClosePath ();
|
||||
c.MoveTo (0f, 375f);
|
||||
c.MoveTo (112f, 351.5f);
|
||||
c.AddQuadCurveToPoint (112f, 342f, 119f, 335f);
|
||||
c.AddLineToPoint (269f, 185f);
|
||||
c.AddQuadCurveToPoint (276f, 179f, 287f, 174f);
|
||||
c.AddQuadCurveToPoint (298f, 169f, 308f, 169f);
|
||||
c.AddLineToPoint (333f, 169f);
|
||||
c.AddQuadCurveToPoint (343f, 169f, 354f, 174f);
|
||||
c.AddQuadCurveToPoint (365f, 179f, 372f, 185f);
|
||||
c.AddLineToPoint (631f, 444f);
|
||||
c.AddQuadCurveToPoint (638f, 451f, 638f, 460.5f);
|
||||
c.AddQuadCurveToPoint (638f, 470f, 631f, 476f);
|
||||
c.AddLineToPoint (581f, 526f);
|
||||
c.AddQuadCurveToPoint (575f, 533f, 565.5f, 533f);
|
||||
c.AddQuadCurveToPoint (556f, 533f, 549f, 526f);
|
||||
c.AddLineToPoint (337f, 313f);
|
||||
c.AddQuadCurveToPoint (330f, 306f, 320.5f, 306f);
|
||||
c.AddQuadCurveToPoint (311f, 306f, 305f, 313f);
|
||||
c.AddLineToPoint (201f, 417f);
|
||||
c.AddQuadCurveToPoint (194f, 424f, 184.5f, 424f);
|
||||
c.AddQuadCurveToPoint (175f, 424f, 169f, 417f);
|
||||
c.AddLineToPoint (119f, 368f);
|
||||
c.AddQuadCurveToPoint (112f, 361f, 112f, 351.5f);
|
||||
c.ClosePath ();
|
||||
c.MoveTo (112f, 351.5f);
|
||||
c.FillPath ();
|
||||
c.StrokePath ();
|
||||
}
|
||||
|
||||
static void icon_question_sign (CGContext c)
|
||||
{
|
||||
c.MoveTo (0f, 375f);
|
||||
c.AddQuadCurveToPoint (0f, 453f, 29.5f, 521f);
|
||||
c.AddQuadCurveToPoint (59f, 589f, 110f, 640f);
|
||||
c.AddQuadCurveToPoint (161f, 691f, 229f, 720.5f);
|
||||
c.AddQuadCurveToPoint (297f, 750f, 375f, 750f);
|
||||
c.AddQuadCurveToPoint (453f, 750f, 521f, 720.5f);
|
||||
c.AddQuadCurveToPoint (589f, 691f, 640f, 640f);
|
||||
c.AddQuadCurveToPoint (691f, 589f, 720.5f, 521f);
|
||||
c.AddQuadCurveToPoint (750f, 453f, 750f, 375f);
|
||||
c.AddQuadCurveToPoint (750f, 297f, 720.5f, 229f);
|
||||
c.AddQuadCurveToPoint (691f, 161f, 640f, 110f);
|
||||
c.AddQuadCurveToPoint (589f, 59f, 521f, 29.5f);
|
||||
c.AddQuadCurveToPoint (453f, 0f, 375f, 0f);
|
||||
c.AddQuadCurveToPoint (297f, 0f, 229f, 29.5f);
|
||||
c.AddQuadCurveToPoint (161f, 59f, 110f, 110f);
|
||||
c.AddQuadCurveToPoint (59f, 161f, 29.5f, 229f);
|
||||
c.AddQuadCurveToPoint (0f, 297f, 0f, 375f);
|
||||
c.ClosePath ();
|
||||
c.MoveTo (0f, 375f);
|
||||
c.MoveTo (250f, 531f);
|
||||
c.AddLineToPoint (294f, 476f);
|
||||
c.AddQuadCurveToPoint (300f, 472f, 304f, 471f);
|
||||
c.AddQuadCurveToPoint (310f, 471f, 314f, 475f);
|
||||
c.AddQuadCurveToPoint (322f, 481f, 332f, 486f);
|
||||
c.AddQuadCurveToPoint (340f, 490f, 350.5f, 493.5f);
|
||||
c.AddQuadCurveToPoint (361f, 497f, 372f, 497f);
|
||||
c.AddQuadCurveToPoint (392f, 497f, 405f, 486.5f);
|
||||
c.AddQuadCurveToPoint (418f, 476f, 418f, 460f);
|
||||
c.AddQuadCurveToPoint (418f, 443f, 406.5f, 429.5f);
|
||||
c.AddQuadCurveToPoint (395f, 416f, 378f, 401f);
|
||||
c.AddQuadCurveToPoint (367f, 392f, 356f, 381.5f);
|
||||
c.AddQuadCurveToPoint (345f, 371f, 336f, 357.5f);
|
||||
c.AddQuadCurveToPoint (327f, 344f, 321f, 327.5f);
|
||||
c.AddQuadCurveToPoint (315f, 311f, 315f, 290f);
|
||||
c.AddLineToPoint (315f, 260f);
|
||||
c.AddQuadCurveToPoint (315f, 255f, 319.5f, 250.5f);
|
||||
c.AddQuadCurveToPoint (324f, 246f, 329f, 246f);
|
||||
c.AddLineToPoint (406f, 246f);
|
||||
c.AddQuadCurveToPoint (412f, 246f, 416f, 250.5f);
|
||||
c.AddQuadCurveToPoint (420f, 255f, 420f, 260f);
|
||||
c.AddLineToPoint (420f, 285f);
|
||||
c.AddQuadCurveToPoint (420f, 303f, 432f, 316f);
|
||||
c.AddQuadCurveToPoint (444f, 329f, 461f, 344f);
|
||||
c.AddQuadCurveToPoint (473f, 354f, 485f, 365.5f);
|
||||
c.AddQuadCurveToPoint (497f, 377f, 506.5f, 392f);
|
||||
c.AddQuadCurveToPoint (516f, 407f, 522.5f, 425f);
|
||||
c.AddQuadCurveToPoint (529f, 443f, 529f, 467f);
|
||||
c.AddQuadCurveToPoint (529f, 499f, 516f, 524f);
|
||||
c.AddQuadCurveToPoint (503f, 549f, 481.5f, 565.5f);
|
||||
c.AddQuadCurveToPoint (460f, 582f, 433f, 590.5f);
|
||||
c.AddQuadCurveToPoint (406f, 599f, 379f, 599f);
|
||||
c.AddQuadCurveToPoint (349f, 599f, 325.5f, 591.5f);
|
||||
c.AddQuadCurveToPoint (302f, 584f, 285.5f, 575f);
|
||||
c.AddQuadCurveToPoint (269f, 566f, 260.5f, 558f);
|
||||
c.AddQuadCurveToPoint (252f, 550f, 251f, 549f);
|
||||
c.AddQuadCurveToPoint (242f, 540f, 250f, 531f);
|
||||
c.ClosePath ();
|
||||
c.MoveTo (250f, 531f);
|
||||
c.MoveTo (315f, 132f);
|
||||
c.AddQuadCurveToPoint (315f, 127f, 319.5f, 122.5f);
|
||||
c.AddQuadCurveToPoint (324f, 118f, 329f, 118f);
|
||||
c.AddLineToPoint (406f, 118f);
|
||||
c.AddQuadCurveToPoint (412f, 118f, 416f, 122.5f);
|
||||
c.AddQuadCurveToPoint (420f, 127f, 420f, 132f);
|
||||
c.AddLineToPoint (420f, 206f);
|
||||
c.AddQuadCurveToPoint (420f, 220f, 406f, 220f);
|
||||
c.AddLineToPoint (329f, 220f);
|
||||
c.AddQuadCurveToPoint (324f, 220f, 319.5f, 216f);
|
||||
c.AddQuadCurveToPoint (315f, 212f, 315f, 206f);
|
||||
c.AddLineToPoint (315f, 132f);
|
||||
c.ClosePath ();
|
||||
c.MoveTo (315f, 132f);
|
||||
c.FillPath ();
|
||||
c.StrokePath ();
|
||||
}
|
||||
|
||||
static void icon_asterisk (CGContext c)
|
||||
{
|
||||
c.MoveTo (1f, 497f);
|
||||
c.AddQuadCurveToPoint (-4f, 515f, 6f, 532f);
|
||||
c.AddLineToPoint (41f, 593f);
|
||||
c.AddQuadCurveToPoint (51f, 610f, 69.5f, 614.5f);
|
||||
c.AddQuadCurveToPoint (88f, 619f, 105f, 610f);
|
||||
c.AddLineToPoint (267f, 516f);
|
||||
c.AddLineToPoint (267f, 703f);
|
||||
c.AddQuadCurveToPoint (267f, 723f, 280.5f, 736.5f);
|
||||
c.AddQuadCurveToPoint (294f, 750f, 314f, 750f);
|
||||
c.AddLineToPoint (383f, 750f);
|
||||
c.AddQuadCurveToPoint (403f, 750f, 416.5f, 736.5f);
|
||||
c.AddQuadCurveToPoint (430f, 723f, 430f, 704f);
|
||||
c.AddLineToPoint (430f, 516f);
|
||||
c.AddLineToPoint (592f, 610f);
|
||||
c.AddQuadCurveToPoint (609f, 619f, 627.5f, 614.5f);
|
||||
c.AddQuadCurveToPoint (646f, 610f, 656f, 593f);
|
||||
c.AddLineToPoint (690f, 532f);
|
||||
c.AddQuadCurveToPoint (700f, 515f, 695.5f, 497f);
|
||||
c.AddQuadCurveToPoint (691f, 479f, 674f, 469f);
|
||||
c.AddLineToPoint (511f, 375f);
|
||||
c.AddLineToPoint (674f, 281f);
|
||||
c.AddQuadCurveToPoint (691f, 271f, 695.5f, 253f);
|
||||
c.AddQuadCurveToPoint (700f, 235f, 691f, 218f);
|
||||
c.AddLineToPoint (656f, 157f);
|
||||
c.AddQuadCurveToPoint (646f, 140f, 627.5f, 135.5f);
|
||||
c.AddQuadCurveToPoint (609f, 131f, 592f, 140f);
|
||||
c.AddLineToPoint (430f, 234f);
|
||||
c.AddLineToPoint (430f, 47f);
|
||||
c.AddQuadCurveToPoint (430f, 27f, 416.5f, 13.5f);
|
||||
c.AddQuadCurveToPoint (403f, 0f, 383f, 0f);
|
||||
c.AddLineToPoint (314f, 0f);
|
||||
c.AddQuadCurveToPoint (294f, 0f, 280.5f, 13.5f);
|
||||
c.AddQuadCurveToPoint (267f, 27f, 267f, 46f);
|
||||
c.AddLineToPoint (267f, 234f);
|
||||
c.AddLineToPoint (105f, 140f);
|
||||
c.AddQuadCurveToPoint (88f, 130f, 69.5f, 135f);
|
||||
c.AddQuadCurveToPoint (51f, 140f, 41f, 157f);
|
||||
c.AddLineToPoint (6f, 218f);
|
||||
c.AddQuadCurveToPoint (-3f, 235f, 1.5f, 253f);
|
||||
c.AddQuadCurveToPoint (6f, 271f, 23f, 281f);
|
||||
c.AddLineToPoint (186f, 375f);
|
||||
c.AddLineToPoint (23f, 469f);
|
||||
c.AddQuadCurveToPoint (6f, 479f, 1f, 497f);
|
||||
c.ClosePath ();
|
||||
c.MoveTo (1f, 497f);
|
||||
c.FillPath ();
|
||||
c.StrokePath ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="xunit.abstractions" version="2.0.0-beta-build2664" targetFramework="MonoTouch10" />
|
||||
<package id="xunit.runner.utility" version="2.0.0-beta-build2664" targetFramework="MonoTouch10" />
|
||||
</packages>
|
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{BB8946FE-4D2A-4FED-8FFD-5DFBA190DB8A}</ProjectGuid>
|
||||
<ProjectTypeGuids>{6BC8ED88-2882-458C-8E55-DFD12B67127B};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>Xunit.Runner.iOS</RootNamespace>
|
||||
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
|
||||
<AssemblyName>xunit.runner.ios</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\iPhone\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;__IOS__;__MOBILE__;LINQ</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<MtouchDebug>true</MtouchDebug>
|
||||
<CodesignKey>iPhone Developer</CodesignKey>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\iPhone\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<CodesignKey>iPhone Developer</CodesignKey>
|
||||
<DefineConstants>__IOS__;__MOBILE__;LINQ</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Options.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Elements\TestCaseElement.cs" />
|
||||
<Compile Include="Elements\TestElement.cs" />
|
||||
<Compile Include="Elements\TestResultElement.cs" />
|
||||
<Compile Include="Elements\TestSuiteElement.cs" />
|
||||
<Compile Include="RunnerOptions.cs" />
|
||||
<Compile Include="TouchRunner.cs" />
|
||||
<Compile Include="TouchViewController.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="MonoTouch.Dialog-1" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="monotouch" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\xunit.abstractions.2.0.0-beta-build2664\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid\xunit.abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="xunit.runner.utility, Version=2.0.0.2664, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\xunit.runner.utility.2.0.0-beta-build2664\lib\monotouch\xunit.runner.utility.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\xunit.runner.xamarin\xunit.runner.xamarin.projitems" Label="Shared" Condition="Exists('..\xunit.runner.xamarin\xunit.runner.xamarin.projitems')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.MonoTouch.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>xunit.runner.xamarin</id>
|
||||
<version>0.99.6</version>
|
||||
<title>xUnit.net [Xamarin iOS/Android Runner]</title>
|
||||
<authors>Oren Novotny</authors>
|
||||
<description>Includes support for running xUnit.net v2 tests on iOS and Android devices</description>
|
||||
<language>en-US</language>
|
||||
<projectUrl>https://github.com/xunit/xunit</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/xunit/media/master/logo-512-transparent.png</iconUrl>
|
||||
<licenseUrl>https://raw.githubusercontent.com/xunit/xunit/master/license.txt</licenseUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<dependencies>
|
||||
<dependency id="xunit.runner.utility" version="[2.0.0-beta]" />
|
||||
<!--<dependency id="xunit.runner.utility" version="[2.0.0-alpha,2.1)" />-->
|
||||
|
||||
<dependency id="xunit.execution" version="[2.0.0-beta]" />
|
||||
<!--<dependency id="xunit.execution" version="[2.0.0-alpha,2.1)" />-->
|
||||
|
||||
<dependency id="xunit" version="[2.0.0-beta]" />
|
||||
<!--<dependency id="xunit" version="[2.0.0-alpha,2.1)" />-->
|
||||
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="lib\MonoTouch\_._" target="lib\MonoTouch\_._" />
|
||||
<file src="lib\MonoAndroid\_._" target="lib\MonoAndriod\_._" />
|
||||
<file src="xunit.runner.ios\NuGet\AppDelegate.Sample.cs" target="content\MonoTouch\AppDelegate.Sample.cs.pp" />
|
||||
<file src="xunit.runner.android\NuGet\MainActivity.Sample.cs" target="content\MonoAndroid\MainActivity.Sample.cs.pp" />
|
||||
</files>
|
||||
</package>
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Xunit.Runners
|
||||
{
|
||||
public enum TestState
|
||||
{
|
||||
NotRun,
|
||||
Passed,
|
||||
Failed,
|
||||
Skipped
|
||||
}
|
||||
|
||||
|
||||
public enum NameDisplay
|
||||
{
|
||||
Short = 1,
|
||||
Full = 2,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Xunit.Runners
|
||||
{
|
||||
interface ITestListener
|
||||
{
|
||||
void RecordResult(MonoTestResult result);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Runners;
|
||||
using Xunit.Runners.UI;
|
||||
|
||||
namespace Xunit.Runners
|
||||
{
|
||||
public class MonoTestCase
|
||||
{
|
||||
|
||||
public event EventHandler TestCaseUpdated;
|
||||
|
||||
private readonly string fqTestMethodName;
|
||||
public string AssemblyFileName { get; private set; }
|
||||
public ITestCase TestCase { get; private set; }
|
||||
|
||||
public MonoTestResult TestResult { get; private set; }
|
||||
|
||||
#if __IOS__ || MAC
|
||||
public string DisplayName { get { return RunnerOptions.Current.GetDisplayName(TestCase.DisplayName, TestCase.Method.Name, fqTestMethodName); } }
|
||||
#else
|
||||
public string DisplayName { get { return RunnerOptions.GetDisplayName(TestCase.DisplayName, TestCase.Method.Name, fqTestMethodName); } }
|
||||
#endif
|
||||
public string UniqueName { get; private set; }
|
||||
|
||||
public MonoTestCase(string assemblyFileName, ITestCase testCase, bool forceUniqueNames)
|
||||
{
|
||||
if (assemblyFileName == null) throw new ArgumentNullException("assemblyFileName");
|
||||
if (testCase == null) throw new ArgumentNullException("testCase");
|
||||
|
||||
fqTestMethodName = String.Format("{0}.{1}", testCase.Class.Name, testCase.Method.Name);
|
||||
UniqueName = forceUniqueNames ? String.Format("{0} ({1})", fqTestMethodName, testCase.UniqueID) : fqTestMethodName;
|
||||
AssemblyFileName = assemblyFileName;
|
||||
TestCase = testCase;
|
||||
|
||||
Result = TestState.NotRun;
|
||||
|
||||
// Create an initial result represnting not run
|
||||
TestResult = new MonoTestResult(this, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public TestState Result { get; private set; }
|
||||
|
||||
|
||||
internal void UpdateTestState(MonoTestResult message)
|
||||
{
|
||||
TestResult = message;
|
||||
|
||||
Output = message.TestResultMessage.Output;
|
||||
Message = null;
|
||||
StackTrace = null;
|
||||
|
||||
if (message.TestResultMessage is ITestPassed)
|
||||
{
|
||||
Result = TestState.Passed;
|
||||
Message = "Passed";
|
||||
}
|
||||
if (message.TestResultMessage is ITestFailed)
|
||||
{
|
||||
Result = TestState.Failed;
|
||||
var failedMessage = (ITestFailed)(message.TestResultMessage);
|
||||
Message = ExceptionUtility.CombineMessages(failedMessage);
|
||||
StackTrace = ExceptionUtility.CombineStackTraces(failedMessage);
|
||||
}
|
||||
if (message.TestResultMessage is ITestSkipped)
|
||||
{
|
||||
Result = TestState.Skipped;
|
||||
|
||||
var skipped = (ITestSkipped)(message.TestResultMessage);
|
||||
Message = skipped.Reason;
|
||||
}
|
||||
}
|
||||
|
||||
// This should be raised on a UI thread as listeners will likely be
|
||||
// UI elements
|
||||
internal void RaiseTestCaseUpdated()
|
||||
{
|
||||
var evt = TestCaseUpdated;
|
||||
if (evt != null)
|
||||
evt(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public string Message { get; private set; }
|
||||
public string Output { get; private set; }
|
||||
public string StackTrace { get; private set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Xunit.Runners
|
||||
{
|
||||
public class MonoTestResult
|
||||
{
|
||||
public MonoTestCase TestCase { get; private set; }
|
||||
public ITestResultMessage TestResultMessage { get; private set; }
|
||||
public TimeSpan Duration { get; set; }
|
||||
|
||||
public string ErrorMessage { get; set; }
|
||||
public string ErrorStackTrace { get; set; }
|
||||
|
||||
public MonoTestResult(MonoTestCase testCase, ITestResultMessage testResult)
|
||||
{
|
||||
if (testCase == null) throw new ArgumentNullException("testCase");
|
||||
TestCase = testCase;
|
||||
TestResultMessage = testResult;
|
||||
|
||||
if(testResult != null)
|
||||
testCase.UpdateTestState(this);
|
||||
}
|
||||
|
||||
internal void RaiseTestUpdated()
|
||||
{
|
||||
TestCase.RaiseTestCaseUpdated();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
// this is an adaptation of NUnitLite's TcpWriter.cs with an additional
|
||||
// overrides and with network-activity UI enhancement
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
#if __IOS__ || MAC
|
||||
#if XAMCORE_2_0
|
||||
using UIKit;
|
||||
#else
|
||||
using MonoTouch.UIKit;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace Xunit.Runners.UI {
|
||||
|
||||
public class TcpTextWriter : TextWriter {
|
||||
|
||||
private TcpClient client;
|
||||
private StreamWriter writer;
|
||||
|
||||
public TcpTextWriter (string hostName, int port)
|
||||
{
|
||||
if (hostName == null)
|
||||
throw new ArgumentNullException ("hostName");
|
||||
if ((port < 0) || (port > UInt16.MaxValue))
|
||||
throw new ArgumentException ("port");
|
||||
|
||||
HostName = hostName;
|
||||
Port = port;
|
||||
|
||||
#if __IOS__ || MAC
|
||||
UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true;
|
||||
#endif
|
||||
try {
|
||||
client = new TcpClient (hostName, port);
|
||||
writer = new StreamWriter (client.GetStream ());
|
||||
}
|
||||
catch {
|
||||
#if __IOS__ || MAC
|
||||
UIApplication.SharedApplication.NetworkActivityIndicatorVisible = false;
|
||||
#endif
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public string HostName { get; private set; }
|
||||
|
||||
public int Port { get; private set; }
|
||||
|
||||
// we override everything that StreamWriter overrides from TextWriter
|
||||
|
||||
public override System.Text.Encoding Encoding {
|
||||
// hardcoded to UTF8 so make it easier on the server side
|
||||
get { return System.Text.Encoding.UTF8; }
|
||||
}
|
||||
|
||||
public override void Close ()
|
||||
{
|
||||
#if __IOS__ || MAC
|
||||
UIApplication.SharedApplication.NetworkActivityIndicatorVisible = false;
|
||||
#endif
|
||||
writer.Close ();
|
||||
}
|
||||
|
||||
protected override void Dispose (bool disposing)
|
||||
{
|
||||
writer.Dispose ();
|
||||
}
|
||||
|
||||
public override void Flush ()
|
||||
{
|
||||
writer.Flush ();
|
||||
}
|
||||
|
||||
// minimum to override - see http://msdn.microsoft.com/en-us/library/system.io.textwriter.aspx
|
||||
public override void Write (char value)
|
||||
{
|
||||
writer.Write (value);
|
||||
}
|
||||
|
||||
public override void Write (char[] buffer)
|
||||
{
|
||||
writer.Write (buffer);
|
||||
}
|
||||
|
||||
public override void Write (char[] buffer, int index, int count)
|
||||
{
|
||||
writer.Write (buffer, index, count);
|
||||
}
|
||||
|
||||
public override void Write (string value)
|
||||
{
|
||||
writer.Write (value);
|
||||
}
|
||||
|
||||
// special extra override to ensure we flush data regularly
|
||||
|
||||
public override void WriteLine ()
|
||||
{
|
||||
writer.WriteLine ();
|
||||
writer.Flush ();
|
||||
}
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче