Bug 11457 - We would like to be able to run Xam.Mac 1.2 FSEvents component under nUnit
Summary: We would like to be able to run Xam.Mac 1.2 FSEvents component under nUnit
Status: RESOLVED FIXED
Alias: None
Product: Xamarin.Mac
Classification: Desktop
Component: Library (Xamarin.Mac.dll) ()
Version: 1.2.x
Hardware: PC Mac OS
: --- normal
Target Milestone: ---
Assignee: Aaron Bockover [MSFT]
URL:
Depends on:
Blocks:
 
Reported: 2013-03-28 06:01 UTC by ian
Modified: 2013-04-22 08:08 UTC (History)
1 user (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description ian 2013-03-28 06:01:56 UTC
Hi,

We were waiting for a long-time for the new Xam.MAc 1.2 release that included FSEvents - it's great and we're very thankful.

We've plugged the component into an nUnit test (we know TextEdit is an issue with FSEvents and want to test this) but we're having problems - the events we see firing in both the sample and our own plugged-in code - do not fire.

We suspect it's related to the OSX message loop, but can't be sure and thus were asked by Xamarin to raise the issue here.

I can't detail all our code here but I can certainly outline the test:

[TestFixtureSetup]
     Call NSApplication.Init (starts run loop)
     Create component that uses fsEvents
     Create events stream and schedule with run Loop
     Start Stream 

[TestMethod]
     Write text to watched path
     Assert callback is called

But nothing is raised back.
Comment 1 ian 2013-03-28 06:23:28 UTC
-	We’ve actually performed more analysis…
-	ANY TEST that uses FSEvents results in the following internal error when running from Xamarin.Studio:

- Test results for nUnitTest configuration Debug
- Internal error
     FileNotFoundExceptlon: Could not load file or assembly or one of its dependencies.
     - Stack Trace 
           at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke (System.Runtime.Remoting.Proxies.RealProxy rp, IMessage msg, System.Exception& exc, System.Object[]& out_args) (OxOOle9] in private/tmp/source/bockbuild/profiles/mono-mac-release
Comment 2 Aaron Bockover [MSFT] 2013-03-28 11:09:55 UTC
FSEventStream requires an NSRunLoop to operate.

I do not believe that it is restricted to the main loop however. You will need to start a background thread (NSThread.Start(() => ...), run a main loop in that thread, and then schedule the FSEventStream with that loop.

See FSEventStream.ScheduleWithRunLoop.
Comment 3 Aaron Bockover [MSFT] 2013-03-28 11:12:49 UTC
Also, if you are already calling ScheduleWithRunLoop, ensure your loop isn't blocked. Loops and unit tests are usually pretty painful to manage. If the loop is blocked, the FSEvents will not be delivered.
Comment 4 ian 2013-04-02 11:54:54 UTC
Hi Aaron,
I'm sure I'm miles off and have some misconceptions but the following won't work, I get an interal error.

I presume I'm not correctly either starting the run loop or referencing it...

using System;
using NUnit.Framework;
using MonoMac.AppKit;
using FSEventsConsole;
using System.Collections.Generic;
using System.IO;
using MonoMac.CoreServices;
using System.Threading;
using MonoMac.Foundation;

namespace FSEventsTest
{
	public class FSWatch
	{
		FSEventStream eventStream;
		TimeSpan eventLatency = new TimeSpan(0,0,5);
		public delegate void FSEventHandler (object sender, FSEventStreamEventsArgs e);
		public event FSEventHandler Changed;
		
		public void Start (string currentWatchPath)
		{
			if (eventStream != null) { Stop(); }
			if (Directory.Exists(currentWatchPath)) 
			{
				eventStream = new FSEventStream(new[] { currentWatchPath }, eventLatency, FSEventStreamCreateFlags.FileEvents);
				eventStream.Events += OnFSEventStreamEvents;
				eventStream.ScheduleWithRunLoop(NSRunLoop.Current); //Can't seem to pass NSThread here...
				eventStream.Start ();
			}
		}
		public void Stop ()
		{
			eventStream.Events -= OnFSEventStreamEvents;
			eventStream.Dispose();
			eventStream = null;
		}
		private void OnFSEventStreamEvents (object sender, FSEventStreamEventsArgs e)
		{
//			Console.WriteLine(e.Events[0].Flags.ToString() + " " + e.Events[0].Path);
			if (Changed != null) { Changed(this, e); }
		}
		public NSThread RunLoopThread;
	}
	[TestFixture]
	public class EventsTests
	{
		List<FSEventStreamEventsArgs> changes = new List<FSEventStreamEventsArgs>();
		[Test]
		public void FSEventTest ()
		{
			var threadStarted = NSThread.Start(() => NSApplication.Init()); //we *think* that's how to start the run loop
			FSWatch watch = new FSWatch(); 
			watch.RunLoopThread = threadStarted;
			watch.Changed += HandleChanged;
			watch.Start("/Users/ianpendert/Desktop");
			File.WriteAllText("/Users/ianpendert/Desktop/" + DateTime.Now, "content");
			Thread.Sleep(2000);

			Assert.IsTrue(changes.Count > 0);
			watch.Stop();
			threadStarted.Dispose();
		}
		void HandleChanged (object sender, FSEventStreamEventsArgs e)
		{
			changes.Add(e);
		}
	}
}
Comment 5 Aaron Bockover [MSFT] 2013-04-04 12:35:09 UTC
NSRunLoop.Current is the run loop associated with the currently executing thread. That is not necessarily your Main run loop. NSRunLoop.Main is the run loop associated with the main thread, which is probably what you want:

eventStream.ScheduleWithRunLoop(NSRunLoop.Main);

Current and Main will be the same if you are currently running on the main thread.
Comment 6 ian 2013-04-08 03:27:21 UTC
Hi Aaron,

I've made the changes... but unfortunately I can't get any test to run using FSEvents under nUnit. Whichever test I try and create the following error is produced in Xamarin.Studio:

- ANY TEST that uses FSEvents results in the following internal error when running from Xamarin.Studio:

Test results for nUnitTest configuration Debug
- Internal error
     FileNotFoundExceptlon: Could not load file or assembly or one of its dependencies.
     Stack Trace
        at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke (System.Runtime.Remoting.Proxies.RealProxy rp, IMessage msg, System.Exception& exc, System.Object[]& out_args) (0x00le9] in /private/tmp/source/bockbuild/profiles/mono-mac-release


This is given before any actual code is run. Is there a non-obvious reference missing?
Comment 7 ian 2013-04-08 03:32:35 UTC
Hi Aaron, 
The above was of course the original problem I reported from a couple of weeks ago. Maybe we can try this a different way around - can YOU send ME a simple nUnit test with FSEvents - then I can see how it's going wrong and get some confidence it actually runs under nUnit.

Thanks 
Ian
Comment 8 ian 2013-04-22 08:08:43 UTC
Hi,

We're finally there. 
We were using the old implementation (via p/invoke) rather than the Xam.Mac namespaced version recently released.
Once plugged in we also found nuances around the OSX file system (namely that detecting changes on xattrib was more accurate than watching for file changes).

All in all thanks for your patience !