Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

It was time to revisit John's changes. As usual, one thing led to about five others. 

An all-source ClarionTest

A while back I took the radical step of converting ClarionTest from an APP file to an all-source project. I wasn't entirely sure at the time why I was doing it. In part it was a wild impulse to get my hands on the source code in a way not possible as long as it remained in an APP. 

...

Note

Don't for a minute think that whatever I'm describing in the following text is the "final" version of ClarionTest. If anything, ClarionTest has been, is and will be in a state of evolution. There are compromises and failures in the current code. When I notice them I'll comment on them, and try give some reason for my choices. And I'll come back to the code periodically and complain about the crap I wrote last time. To paraphrase Anil Dash, writing code is all about trying to suck less. That doesn't mean I'm not ever happy with my code - I frequently sit back and look at something I've written with a great deal of satisfaction. But I know that just because it looks good now doesn't mean it will look equally good down the road.

 

Starting over (again and again)

 

After commenting out all of the existing code and many of the data declarations, I had a bare bones, non-working app. So what were my requirements?

...

Now, how about loading up the test DLL?  

 

 

Really this just takes two lines of code:

Code Block
			TestRunner.Init(TestDllName)
			TestRunner.GetTestProcedures(ProceduresQ)

TestRunner loads up the test procedures into a predefined queue that's part of the ClarionTest classes. I am going to want to do some formatting of the queue, which raises an important question about where those kinds of changes should be made. If I want to use my TestRunner's queue for display purposes then I'm going to need to add some UI-related fields into the queue (for colors, styles, etc). That's inevitably going to make things messy and will blur the lines between the display of testing information to the user and the code that actually execute tests. 

I decided pretty quickly to follow the approach previously used in ClarionTest, which was to create a separate display queue in the application itself, and copy the relevant information over from the queue populated by the TestRunner object. That's a bit of extra work, but it keeps UI separate from business logic and it means I don't have to make changes to my class library to handle UI improvements. 

Here's the LoadTests routine with TestsQ as the UI queue and ProceduresQ as the list of test procedures:

Code Block
LoadTests                               ROUTINE
	do SetDirectoryWatcher
	free(TestsQ)
	logger.Write('Loading tests from ' & TestDllPathAndName)
	str.Assign(TestDllPathAndName)
	TestDllDirectory = str.SubString(1,str.LastIndexOf('\'))
	if TestDllDirectory <> ''
		if not exists(TestDllDirectory)
			message('The test directory ' & TestDllDirectory & ' does not exist')
		ELSE
			setpath(TestDllDirectory)
			TestDllName = str.SubString(str.LastIndexOf('\') + 1,str.Length())
			TestRunner.Init(TestDllName)
			TestRunner.GetTestProcedures(ProceduresQ)
			logger.Write('Found ' & records(ProceduresQ) & ' tests')
		END
	END
	loop x = 1 to records(ProceduresQ)
		get(ProceduresQ,x)
		IF ProceduresQ.TestGroupName <> PreviousGroupOrTestName
			PreviousGroupOrTestName = ProceduresQ.TestGroupName
			CLEAR(TestsQ)
			TestsQ.GroupOrTestName = ProceduresQ.TestGroupName
			TestsQ.GroupOrTestLevel = 1
			TestsQ.ProcedureQIndex = 0
			TestsQ.GroupOrTestStyle = Style:Bold
			ADD(TestsQ)
		END
		TestsQ.GroupOrTestStyle = Style:Default
		TestsQ.GroupOrTestName = ProceduresQ.TestName
		TestsQ.GroupOrTestLevel = 2
		TestsQ.ProcedureQIndex = x
		TestsQ.TestResult = ''
		TestsQ.TestResultStyle = Style:Default
		TestsQ.Mark = false
		Add(TestsQ)
	END

The extra ADD at the top half of the loop is to handle test groups, e.g.:

Image Added

Directory monitoring

With most of the core functionality in place, I still needed to re-implement directory monitoring which uses the DCL_System_Runtime_DirectoryWatcher class. The original class was written by Alan Telford (building on work by Jim Kane) and is described in Clarion Magazine article Waiting For Files With Clarion Threads. I've made a few minor changes, including moving the suggested event trapping code into a TakeEvent() method which you place in the Accept loop. Then you just need to derive the DoTask method which will be triggered any time a file in the monitored directory changes. 

Initially I run the unit tests any time there was a directory change, but this had two problems. First, I was getting a number of events each time I built a new test DLL and copied it into my UnitTests directory. I really only wanted a single event. My initial solution is a bit of a hack: I set a variable called TimeOfLastDirectoryChange whenever a change is detected, and then I use the following code to delay the test execution until I've had a quiet time of somewhere around a second (the value of DelayBeforeAutoRun is configurable via INI file). 

Code Block
			if TimeOfLastDirectoryChange > 0
				if clock() < TimeOfLastDirectoryChange or clock() >  TimeOfLastDirectoryChange + DelayBeforeAutorun
					TimeOfLastDirectoryChange = 0
					do RunTests
				end
			end

That's a bit of a hack. What I'd really like is a way to handle this inside the class itself, but I haven't come up with a clean solution yet. 

The other problem is that the directory watcher is indiscriminate - it triggers when any file changes. One of my unit tests updates an INI file, and each time the INI file is updated the tests are triggered, so ClarionTests sits in a loop executing all the selected tests every second (provided directory monitoring is enabled). 

Clearly I need to filter out only the DLL changes. I'm not sure of the best way to do this, although if nothing else I may yet use my directory class. 

Also I'm not too impressed with the code I'm using to manage the on-screen list of test procedures.  I need to add icons and some more sophisticated marking code, and I can see that becoming a mess in a hurry. 

Still work to be done

There's always still work to be done. Besides monitoring for DLL changes only, I need to find some way of presenting summary information and showing only, say, failed tests. The UI is pretty ugly, so if you have some suggestions for how it can be improved please pass them along. Oh, and not all the buttons work yet. Some of that may have to wait until next week. 

Meanwhile, ClarionTest is now free of any and all third party dependencies. While that has resulted in some loss of functionality (at least over the short term) it does mean that it's now much easier to build ClarionTest. You don't need to download or register any additional templates. Just load up the cwproj and compile. 

As always you can download the latest and greatest DCL including ClarionTest at GitHub.