Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,8 @@ protected static bool MonitorAdbLogcat (Func<string, bool> action, string logcat
{
string ext = Environment.OSVersion.Platform != PlatformID.Unix ? ".exe" : "";
string adb = Path.Combine (AndroidSdkPath, "platform-tools", "adb" + ext);
var info = new ProcessStartInfo (adb, "logcat") {
string adbTarget = Environment.GetEnvironmentVariable ("ADB_TARGET");
var info = new ProcessStartInfo (adb, $"{adbTarget} logcat") {
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;

using Android.App;
using Android.OS;
using Android.Runtime;
using Android.Util;

using Java.InteropTests;

namespace ${ROOT_NAMESPACE};

[Register ("${JAVA_PACKAGENAME}.MainActivity"), Activity (Label = "${PROJECT_NAME}", MainLauncher = true)]
public class MainActivity : Activity
{
const string ResultPrefix = "INTERFACE_COLLECTION_ROOTING_RESULT";
const string ResultToken = "${RESULT_TOKEN}";
const string Tag = "InterfaceCollections";

protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);

try {
using var holder = new RawInterfaceCollectionHolder ();
using var list = (IDisposable) holder.CreateList ();
using var collection = (IDisposable) holder.CreateCollection ();
using var dictionary = (IDisposable) holder.CreateInterfaceDictionary ();
Log.Info (Tag, $"{ResultPrefix} PASS {ResultToken}");
} catch (Exception e) {
Log.Error (Tag, $"{ResultPrefix} FAIL {ResultToken}: {e}");
} finally {
Finish ();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Reference-wrapper subgraph from dotnet-android build 1585926, with readable node IDs. -->
<DirectedGraph xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
<Node Id="peer-metadata" Label="Type metadata: [Java.Interop]Java.Interop.IJavaPeerable" />
<Node Id="list-factory" Label="Mono_Android_Java_Interop_SafeJavaCollectionFactory__CreateReferenceListFromJniHandle" />
<Node Id="collection-factory" Label="Mono_Android_Java_Interop_SafeJavaCollectionFactory__CreateReferenceCollectionFromJniHandle" />
<Node Id="dictionary-factory" Label="Mono_Android_Java_Interop_SafeJavaCollectionFactory__CreateReferenceDictionaryFromJniHandle" />
<Node Id="dictionary-type" Label="_ZTV127Mono_Android_Android_Runtime_JavaDictionary_2&lt;Java_Interop_Java_Interop_IJavaPeerable__Java_Interop_Java_Interop_IJavaPeerable&gt; constructed" />
<Node Id="dictionary-canonical" Label="Mono_Android_Android_Runtime_JavaDictionary_2&lt;System___Canon__System___Canon&gt;___ctor_0" />
<Node Id="collection-type" Label="_ZTV86Mono_Android_Android_Runtime_JavaCollection_1&lt;Java_Interop_Java_Interop_IJavaPeerable&gt; constructed" />
<Node Id="collection-canonical" Label="Mono_Android_Android_Runtime_JavaCollection_1&lt;System___Canon&gt;___ctor" />
<Node Id="list-type" Label="_ZTV80Mono_Android_Android_Runtime_JavaList_1&lt;Java_Interop_Java_Interop_IJavaPeerable&gt; constructed" />
<Node Id="list-canonical" Label="Mono_Android_Android_Runtime_JavaList_1&lt;System___Canon&gt;___ctor_0" />
<Node Id="list-dictionary" Label="__GenericDict_Mono_Android_Android_Runtime_JavaList_1&lt;Java_Interop_Java_Interop_IJavaPeerable&gt;" />
<Node Id="list-constructor" Label="[Mono.Android]Android.Runtime.JavaList`1&lt;Java.Interop.IJavaPeerable&gt;..ctor(native int,JniHandleOwnership) backed by Mono_Android_Android_Runtime_JavaList_1&lt;System___Canon&gt;___ctor_0" />
<Node Id="collection-dictionary" Label="__GenericDict_Mono_Android_Android_Runtime_JavaCollection_1&lt;Java_Interop_Java_Interop_IJavaPeerable&gt;" />
<Node Id="collection-constructor" Label="[Mono.Android]Android.Runtime.JavaCollection`1&lt;Java.Interop.IJavaPeerable&gt;..ctor(native int,JniHandleOwnership) backed by Mono_Android_Android_Runtime_JavaCollection_1&lt;System___Canon&gt;___ctor" />
<Node Id="dictionary-dictionary" Label="__GenericDict_Mono_Android_Android_Runtime_JavaDictionary_2&lt;Java_Interop_Java_Interop_IJavaPeerable__Java_Interop_Java_Interop_IJavaPeerable&gt;" />
<Node Id="dictionary-constructor" Label="[Mono.Android]Android.Runtime.JavaDictionary`2&lt;Java.Interop.IJavaPeerable,Java.Interop.IJavaPeerable&gt;..ctor(native int,JniHandleOwnership) backed by Mono_Android_Android_Runtime_JavaDictionary_2&lt;System___Canon__System___Canon&gt;___ctor_0" />
<Node Id="dictionary-conditional" Label="(Mono_Android_Java_Interop_SafeJavaCollectionFactory__CreateReferenceDictionaryFromJniHandle, Type metadata: [Java.Interop]Java.Interop.IJavaPeerable)" />
<Node Id="collection-conditional" Label="(Mono_Android_Java_Interop_SafeJavaCollectionFactory__CreateReferenceCollectionFromJniHandle, Type metadata: [Java.Interop]Java.Interop.IJavaPeerable)" />
<Node Id="list-conditional" Label="(Mono_Android_Java_Interop_SafeJavaCollectionFactory__CreateReferenceListFromJniHandle, Type metadata: [Java.Interop]Java.Interop.IJavaPeerable)" />
<Node Id="list-dependency" Label="(__GenericDict_Mono_Android_Android_Runtime_JavaList_1&lt;Java_Interop_Java_Interop_IJavaPeerable&gt;, Mono_Android_Android_Runtime_JavaList_1&lt;System___Canon&gt;___ctor_0)" />
<Node Id="collection-dependency" Label="(__GenericDict_Mono_Android_Android_Runtime_JavaCollection_1&lt;Java_Interop_Java_Interop_IJavaPeerable&gt;, Mono_Android_Android_Runtime_JavaCollection_1&lt;System___Canon&gt;___ctor)" />
<Node Id="dictionary-dependency" Label="(__GenericDict_Mono_Android_Android_Runtime_JavaDictionary_2&lt;Java_Interop_Java_Interop_IJavaPeerable__Java_Interop_Java_Interop_IJavaPeerable&gt;, Mono_Android_Android_Runtime_JavaDictionary_2&lt;System___Canon__System___Canon&gt;___ctor_0)" />
</Nodes>
<Links>
<Link Source="peer-metadata" Target="dictionary-conditional" Reason="Secondary" />
<Link Source="peer-metadata" Target="collection-conditional" Reason="Secondary" />
<Link Source="peer-metadata" Target="list-conditional" Reason="Secondary" />
<Link Source="list-factory" Target="list-conditional" Reason="Primary" />
<Link Source="collection-factory" Target="collection-conditional" Reason="Primary" />
<Link Source="dictionary-factory" Target="dictionary-conditional" Reason="Primary" />
<Link Source="dictionary-type" Target="dictionary-dictionary" Reason="reloc" />
<Link Source="dictionary-canonical" Target="dictionary-dependency" Reason="Secondary" />
<Link Source="collection-type" Target="collection-dictionary" Reason="reloc" />
<Link Source="collection-canonical" Target="collection-dependency" Reason="Secondary" />
<Link Source="list-type" Target="list-dictionary" Reason="reloc" />
<Link Source="list-canonical" Target="list-dependency" Reason="Secondary" />
<Link Source="list-dictionary" Target="list-dependency" Reason="Primary" />
<Link Source="collection-dictionary" Target="collection-dependency" Reason="Primary" />
<Link Source="dictionary-dictionary" Target="dictionary-dependency" Reason="Primary" />
<Link Source="dictionary-conditional" Target="dictionary-type" Reason="newobj" />
<Link Source="collection-conditional" Target="collection-type" Reason="newobj" />
<Link Source="list-conditional" Target="list-type" Reason="newobj" />
<Link Source="list-dependency" Target="list-constructor" Reason="Generic dictionary dependency" />
<Link Source="collection-dependency" Target="collection-constructor" Reason="Generic dictionary dependency" />
<Link Source="dictionary-dependency" Target="dictionary-constructor" Reason="Generic dictionary dependency" />
</Links>
</DirectedGraph>
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using System.IO;
using System.Linq;
using System.Xml.Linq;

using NUnit.Framework;
using Xamarin.ProjectTools;

namespace Xamarin.Android.Build.Tests;

[TestFixture]
public class InterfaceCollectionRootingTests : BaseTest
{
static readonly XNamespace DgmlNamespace = "http://schemas.microsoft.com/vs/2009/dgml";

static string GraphPath => Path.Combine (
XABuildPaths.TopDirectory, "tests", "MSBuildDeviceIntegration", "Resources", "InterfaceCollectionApp", "rooting.dgml.xml");

[Test]
public void CanonicalWrapperRootingGraph ()
{
var path = TestContext.Parameters.Get ("InterfaceCollectionRootingGraph", GraphPath);
InterfaceCollectionTests.AssertCanonicalWrapperRooting (path);
}

[TestCase ("list")]
[TestCase ("collection")]
[TestCase ("dictionary")]
public void DirectFactoryRooting (string wrapper)
{
var graph = XDocument.Load (GraphPath);
UseDirectFactoryRoot (graph, wrapper);
AssertGraph (graph);
}

[TestCase ("dictionary-factory", "conditional factory primary dependency")]
[TestCase ("peer-metadata", "conditional factory metadata dependency")]
public void RejectsMissingConditionalInput (string source, string message)
{
var graph = XDocument.Load (GraphPath);
FindLink (graph, source, "dictionary-conditional").Remove ();
Assert.That (() => AssertGraph (graph), Throws.TypeOf<AssertionException> ().With.Message.Contains (message));
}

[TestCase ("dictionary-factory", "conditional factory primary dependency")]
[TestCase ("peer-metadata", "conditional factory metadata dependency")]
public void RejectsWrongConditionalInput (string source, string message)
{
var graph = XDocument.Load (GraphPath);
FindLink (graph, source, "dictionary-conditional").SetAttributeValue ("Source", "collection-factory");
Assert.That (() => AssertGraph (graph), Throws.TypeOf<AssertionException> ().With.Message.Contains (message));
}

[TestCase (false)]
[TestCase (true)]
public void RejectsMissingAllocation (bool conditional)
{
var graph = XDocument.Load (GraphPath);
if (!conditional) {
UseDirectFactoryRoot (graph, "dictionary");
}
FindLink (graph, conditional ? "dictionary-conditional" : "dictionary-factory", "dictionary-type").Remove ();
Assert.That (() => AssertGraph (graph), Throws.TypeOf<AssertionException> ().With.Message.Contains ("newobj dependency was not found"));
}

[TestCase (false)]
[TestCase (true)]
public void RejectsUnexpectedRoot (bool conditional)
{
var graph = XDocument.Load (GraphPath);
if (!conditional) {
UseDirectFactoryRoot (graph, "dictionary");
}
var unexpectedLink = new XElement (FindLink (
graph, conditional ? "dictionary-conditional" : "dictionary-factory", "dictionary-type"));
unexpectedLink.SetAttributeValue ("Source", "collection-factory");
graph.Descendants (DgmlNamespace + "Links").Single ().Add (unexpectedLink);
Assert.That (() => AssertGraph (graph), Throws.TypeOf<AssertionException> ().With.Message.Contains ("unexpected incoming dependency"));
}

[TestCase ("dictionary-factory")]
[TestCase ("dictionary-conditional")]
public void RejectsAmbiguousFactoryNodes (string nodeId)
{
var graph = XDocument.Load (GraphPath);
var duplicate = new XElement (FindNode (graph, nodeId));
duplicate.SetAttributeValue ("Id", "duplicate");
graph.Descendants (DgmlNamespace + "Nodes").Single ().Add (duplicate);
Assert.That (() => AssertGraph (graph), Throws.TypeOf<AssertionException> ().With.Message.Contains ("ambiguous node matches"));
}

static void UseDirectFactoryRoot (XDocument graph, string wrapper)
{
FindLink (graph, $"{wrapper}-conditional", $"{wrapper}-type").SetAttributeValue ("Source", $"{wrapper}-factory");
FindLink (graph, $"{wrapper}-factory", $"{wrapper}-conditional").Remove ();
FindLink (graph, "peer-metadata", $"{wrapper}-conditional").Remove ();
FindNode (graph, $"{wrapper}-conditional").Remove ();
}

static XElement FindNode (XDocument graph, string id)
{
return graph.Descendants (DgmlNamespace + "Node").Single (node => node.Attribute ("Id")?.Value == id);
}

static XElement FindLink (XDocument graph, string source, string target)
{
return graph.Descendants (DgmlNamespace + "Link").Single (
link => link.Attribute ("Source")?.Value == source && link.Attribute ("Target")?.Value == target);
}

static void AssertGraph (XDocument graph)
{
var path = Path.GetTempFileName ();
try {
graph.Save (path);
InterfaceCollectionTests.AssertCanonicalWrapperRooting (path);
} finally {
File.Delete (path);
}
}
}
Loading