WaitOnFileExStep additions

Topics: Developer Forum
Aug 10, 2009 at 6:03 PM

For my current project I took the liberty of adjusting the original step to fit our needs. I implemented the following changes:

  • File filter is also applied to old files instead of the default xml filter (*.xml);
  • In case of an old file, the full path was used as value for both context properties instead of a combination of a file name and a full path;
  • In case of an old file, the file is not deleted even though the Delete property was set.

I hope you can use some of the changes in your project. Below is the full class. Please let me know what you think.

//---------------------------------------------------------------------
// File: WaitOnFileExStep.cs
// 
// Summary: 
//
// Copyright (c) Hammersmith & Fulham Bridge Partnership. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//---------------------------------------------------------------------

using System;
using System.IO;
using System.Threading;
using System.Xml;

namespace BizUnit.Extensions
{
	/// <summary>
	/// The WaitOnFileExStep is used to wait for a FILE to be written to a given location.
	/// It uses the file system watcher. It replaces the old step which used to simply delay
	/// for the given interval.Now when the file is detected the system returns immediately.
	/// It also loads the file name(both the full name and the simple name) into the context 
	/// (with the keynames WaitedFileFullName and WaitedFileName) and allows you to specify whether you want 
	/// to consider previously existing files which is useful in scenarios where the testing is
	/// intensive and the system races ahead of the test and places the file in the location and
	/// the step executes after the file is already available.
	/// </summary>
	/// 
	/// <remarks>
	/// The following shows an example of the Xml representation of this test step.
	/// 
	/// <code escaped="true">
	///	<TestStep assemblyPath="BizUnit.Extensions.dll" typeName="BizUnit.Extensions.WaitOnFileExStep">
	///		<Path>n:\\</Path>
	///		<FileFilter>*.xml</FileFilter>
	///		<TimeOut>10000</TimeOut>
	///		<IncludeOldFiles>Y</IncludeOldFiles>
	///		<Delete>Y</Delete>
	///	</TestStep>
	///	</code>
	///	
	///	<list type="table">
	///		<listheader>
	///			<term>Tag</term>
	///			<description>Description</description>
	///		</listheader>
	///		<item>
	///			<term>Path</term>
	///			<description>The directory to look for the FILE</description>
	///		</item>
	///		<item>
	///			<term>FileFilter</term>
	///			<description>The FILE mask to be used to search for a FILE, e.g. *.xml</description>
	///		</item>
	///		<item>
	///			<term>TimeOut</term>
	///			<description>The time to wait for the FILE to become present in miliseconds</description>
	///		</item>
	///		<item>
	///			<term>Delete</term>
	///			<description>Do you want to delete the file when checked (Y/N)</description>
	///		</item>
	///		<item>
	///		<term>IncludeOldFiles</term>
	///		<description>should we take into account files already existing</description>
	///		</item>
	///	</list>
	///	</remarks>

	public class WaitOnFileExStep: ITestStep
	{
		ManualResetEvent mre;
		string newFilePath;
		string newFileName;

		/// <summary>
		/// ITestStep.Execute() implementation
		/// </summary>
		/// <param name='testConfig'>The Xml fragment containing the configuration for this test step</param>
		/// <param name='context'>The context for the test, this holds state that is passed beteen tests</param>
		public void Execute(XmlNode testConfig, Context context)
		{
			// read test config...
			string path = context.ReadConfigAsString( testConfig, "Path" );
			string fileFilter = context.ReadConfigAsString( testConfig, "FileFilter" );
			int timeOut = context.ReadConfigAsInt32( testConfig, "TimeOut" );
			string deleteFileIfFound = context.ReadConfigAsString(testConfig,"Delete",true);
			string includeOldFiles = context.ReadConfigAsString(testConfig,"IncludeOldFiles");

            string foundPath = null;
            if (includeOldFiles == "Y")
                foundPath = SearchForOldFile(path, fileFilter);
            if (foundPath == null)
                foundPath = WaitForFile(context, path, fileFilter, timeOut);
            if (foundPath == null)
                throw new Exception(string.Format("WaitOnFileStep timed out after {0} milisecs watching path:{1}, filter{2}", timeOut, path, fileFilter));

            AddFileInformationToContext(context, foundPath);

			if (deleteFileIfFound == "Y")
                DeleteFile(context, foundPath);

            XmlNode validationConfig = testConfig.SelectSingleNode("ValidationStep");
            context.ExecuteValidator(null, validationConfig);

            return;
		}

        private static void DeleteFile(Context context, string foundPath)
        {
            Thread.Sleep(500); // Wait for file to be closed by creator
            File.Delete(foundPath);
            context.LogWarning("FILE {0} HAS BEEN DELETED", foundPath);
        }

        private static void AddFileInformationToContext(Context context, string path)
        {
            context.Add("WaitedFileFullName", path, true);
            context.Add("WaitedFileName", Path.GetFileName(path), true);
            return;
        }

        private string WaitForFile(Context context, string path, string fileFilter, int timeOut)
        {
            FileSystemWatcher watcher = new FileSystemWatcher();
            watcher.Path = path;
            watcher.Filter = fileFilter;
            watcher.NotifyFilter = NotifyFilters.LastWrite;
            watcher.EnableRaisingEvents = true;
            watcher.IncludeSubdirectories = false;
            watcher.Changed += new FileSystemEventHandler(OnCreated);
            mre = new ManualResetEvent(false);

            if (!mre.WaitOne(timeOut, false))
                return null;

            context.LogInfo(string.Format("WaitOnFileStep found the file: {0}", newFilePath));
            return newFilePath;
        }
        private static string SearchForOldFile(string path, string fileFilter)
        {
            // Set newFilename and newFilePath with the earliest file
            string[] matches = Directory.GetFiles(path, fileFilter);
            if (matches.Length == 0)
                return null;
            
            return matches[0];
        }

		private void OnCreated(object sender, FileSystemEventArgs e) 
		{ 
			newFilePath = e.FullPath;
			newFileName = e.Name;
			mre.Set();
		}
	}
}


 

Coordinator
Aug 10, 2009 at 6:46 PM

Hi,

Thank you for this. It addresses an outstanding bug as well . I will put this into the code base.

Cheers

Benjy