Initial check-in of Xamarin runner apps

This commit is contained in:
Oren Novotny 2014-05-26 13:10:29 -04:00
Коммит 416ed4ec4d
138 изменённых файлов: 15294 добавлений и 0 удалений

28
.gitattributes поставляемый Normal file
Просмотреть файл

@ -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

23
.gitignore поставляемый Normal file
Просмотреть файл

@ -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

13
license.txt Normal file
Просмотреть файл

@ -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>

Двоичные данные
src/common/Application.ico Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 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")]

47
src/common/Guard.cs Normal file
Просмотреть файл

@ -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");
}
}
}

12
src/xunit.ruleset Normal file
Просмотреть файл

@ -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 &copy; 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.

182
src/xunit.runner.android/Resources/Resource.Designer.cs сгенерированный Normal file
Просмотреть файл

@ -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

Двоичные данные
src/xunit.runner.android/Resources/drawable/dialog_disclosure.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 289 B

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 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 ();
}
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше