Dear Ralph,
you asked about a clock faking test resource. A while ago I developed a ClassClonerTestResource, this allows you to take a current class which is normally a singleton that is in use, e.g. DateAndTime, and to clone it especially for testing purposes so as to be able to test the clone separately. This would allow me to set my clock under test to some crucial rollover points and test its behaviour. I developed this especially to test bug fix 474 on mantis. [ Edgar/Ralph - here is a fix with tests for you!] However what this doesn't do is replace DateAndTime, so if you want to run code which calls "DateAndTime now" and have it do something clever like run last week, or run backwards it will not work. However you raise an interesting problem which I think it may be worth solving for everyone, and I think that as a result of my recent discovery of the amazing ProcessLocalVariables I may have an answer. At present ProcessLocalVariables only support value and #value: As it happens I added a #doesNotUnderstand: handler as an experiment in allowing gjallar Sessions to spoof as MagmaSessions for Magma. At the time I thought that this would have some interesting possibilities. For us it means that a ProcessLocalVariable can spoof a class for us within a defined scope (process or block). ProcessSpecificVariable-doesNotUnderstand: aMessage ^ aMessage sendTo: self value So I made a DateAndTimeWarp a subclass of DynamicVariable (a variant of the ProcessLocalVariable which has block scope as well as process scope) and set the default to return DateAndTime. We now have a psuedo DateAndTime "class" which is pluggable on a per process basis. If we swap DateAndTime in the system dictionary for DateAndTimeWarp, it works! (i.e. nothing breaks!!!) DateAndTimeWarp-installImposter (DateAndTime respondsTo: #installImposter) ifTrue: [ self error: 'DateAndTime is already spoofed' ]. DateAndTimeReal := Smalltalk at: #DateAndTime. Smalltalk at: #DateAndTime put: self. Now we can substitute our clone with some modifications. The fixed DateAndTime implementation available on mantis via "Installer mantis fixBug: 474." (tested in 3.8 and Gjallar0,4) has already been refactored to refer to its ClassVar "ClockProvider" for all clock references. (default setting is class Time) So all we need to do is replace ClockProvider, we will replace it with an instance of DateAndTimeWarp, DateAndTimeWarp-class-warpedClockBeginAt: t speed: s | newClock newSystemClockProvider | newSystemClockProvider := self new setStart: t speed: s. newClock := ClassClonerTestResource cloneOf: DateAndTimeReal. newClock classPool at: #ClockProvider put: newSystemClockProvider. clock initializeOffsets. ^ newClock We instantiate our new clock provider with a start time, (nil for now), and a speed. The new clock provider saves some offsets, and re-implements #totalSeconds, #millisecondClockValue and #secondsWhenClockTicks (this method waits until the clock ticks and is used for syncing the seconds and milliseconds clocks together. The new clock is substituted for the default clock with the following code: DateAndTimeWarp-class-beginAt: start warpSpeed: s during: aBlock ^ self value: (self warpedClockBeginAt: start speed: s) during: aBlock To Test: DateAndTime beginAt: DateAndTime yesterday warpSpeed: 3 during: [ 10 timesRepeat: [ Transcript cr; show: DateAndTime now. (Delay forSeconds: 1) wait. ] ]. I thought that was pretty impressive stuff. I am well impressed with these process local variables! Also speed wise: au natural, DateAndTime nowBenchmark. '6.84 µs' (I did improve DateAndTime now by a factor of 50-80 in fix 474) The equivalent benchmark spoofed using process local variable DynamicVariable, benchmark gives: 29.09 µs, i.e. not slow. This code is available in Gjallar monticello repository 'http://mc.gjallar.se' in the ProcessSpecific package. enjoy Keith |
Free forum by Nabble | Edit this page |