Hamcrest and the Google Web Toolkit

90 views
Skip to first unread message

Pascal Muetschard

unread,
Aug 25, 2008, 7:40:46 PM8/25/08
to Hamcrest Developers
Hi Hamcrest Developers,

I've wanted to use Hamcrest with GWT and so I have worked on porting
the core and library functionality to GWT. Attached is a patch that
provides exactly that. Can you guys review this, give feedback on it,
and - hopefully - eventually get it committed? The patch is taken from
revision 292 from within http://hamcrest.googlecode.com/svn/trunk.

Below is a short list of explanations for some of the changes. Note
that no functional/API changes were made.

Changes in Core:
- Added an exclude property to the Factory annotation that will allow
constructor methods to exclude themselves from the GWT target, if they
use classes that are not translatable (see .core.Is)
- Added a "delegator" in internal for the java.lang.reflect.Array
class, since that class is not part of GWT. The .internal.ArrayAccess
class simply delegates all its methods to the java.lang.reflect.Array
class, whereas the GWT jar will replace it with its own
implementation. This delegator is used by the core.IsEqual
and .internal.ArrayIterator classes.
- Removed the dependancy on regex in the org.hamcrest.core.DescribedAs
class.

Changes in Generator:
- Added the funcationality described by the change to the Factory
annotation class above. The tool now takes an optional target
parameter, which is then checked against the exclude list of each
factory method.

Changes in Library:
- Since GWT does not support reflection, the TypeSafeMatcher
functionality can not be (directly) supported. However, since there
are many useful matchers extending this class, the GWT jar will
replace it with an "empty" class and will then "inject" classes in
between the current hierarchy. To allow for this injection, empty
classes were added that sit in between the TypeSafeMatcher parent
class and the implementing matchers. For example, the hierarchy for
the IsArray matcher was changed from
IsArray -> TypeSafeMatcher -> BaseMatcher
to
IsArray -> ArrayTypeSafeMatcher -> TypeSafeMatcher -> BaseMatcher.
This allows GWT to replace ArrayTypeSafeMatcher with its own version,
which then knows that it is checking for an array at compile time
(similarily, the StringTypeSafeMatcher knows to check for a String,
etc.)
- Because Character's isWhitespace method is not part of GWT, a
delegator was added to the StringTypeSafeMatcher for this. This is for
the IsEqualIgnoringWhiteSpace matcher.
- GWT does not support the cloning of arrays, so every instance of
clone on an array was replaced by constructing a new array and
System.arraycopying in the values. This should have the same end
effect, since the clone on the array is shallow in the JVM.

Thanks

--
Pascal Muetschard

Pascal Muetschard

unread,
Aug 25, 2008, 7:46:43 PM8/25/08
to Hamcrest Developers
Wow, this never gets old... Here's the patch.
--
Pascal Muetschard
patch-gwt-1

Nat Pryce

unread,
Aug 26, 2008, 12:13:36 PM8/26/08
to hamcre...@googlegroups.com
I think that supporting GWT is an excellent idea. It would be great
if GWT was a first-class supported platform, alongside plain ol' Java,
PHP, Objective C, Python and the rest.

I have some concerns (sorry, not had time to look through the patch yet).

1) The TypeSafeMatcher implements vital functionality -- type checking
and null checking. How do you make the matchers safe with a
do-nothing implementation? Perhaps we need to revisit how that is
implemented and, instead, force subclasses to pass the expected class
to the super constructor. The whole injection thing makes me
uncomfortable -- it looks like a potential maintenance and testing
problem for the future. Which brings me to...

2) How can we ensure future changes to Hamcrest don't break the GWT
version? Can we make the build compile to Javascript and run tests in
GWT somehow?

--Nat

2008/8/26 Pascal Muetschard <pmuet...@google.com>:

Pascal Muetschard

unread,
Aug 26, 2008, 12:51:45 PM8/26/08
to hamcre...@googlegroups.com
Thank you for your feedback. Here are some clarifications:
0. About making the GWT version independent of the Java version. There is actually nearly no GWT only code in my patch. The only "GWT only" code is for the TypeSafeMatcher things, which I will expand on later. From core, the only thing needed to make GWT work was to "override" the implementation of StringDescription. If we do make GWT's version independent, I see a lot of duplication of effort.

1. About TypeSafeMatcher. Yes, checking for the right type and null is an important part of those matchers and is, of course, maintained in GWT as well - we wouldn't want the matchers to fail with an ClassCastException... Here is the problem: GWT does not have reflection (or at least only very very partial reflection). So, this means that Class.isAssignableFrom and Class.isInstance are not available. On the other hand, the "instanceof" operator is. Now, to use the instanceof operator, we need to know the type at compile time. The funny thing is, for all of the library matchers that extend TypeSafeMatcher, we do know the type at compile time. So, in order to not loose the functionality of all those matchers, I added "intermediate" classes, such as StringTypeSafeMatcher, which explicitly state at compile time what type they are looking for. This means that when running in plain old Java, the TypeSafeMatcher does all the work using reflection (as it does now) and the StringTypeSafeMatcher and its cohorts are empty. Whereas for GWT, the TypeSafeMatcher is empty and the StringTypeSafeMatcher and its cohorts do all the work since at compile time they will use "item instanceof <whatever>". I hope that makes more sense.

2. This is a very good question. I have been thinking about this and the best solution would be to do a JS compile of the GWT library in ant. However, this means adding a dependency in the build libraries on GWT (which might not be that bad) and at least an additional 30seconds of build time, which sucks since a clean build including running junit currently takes less than 10 seconds. If we can live with both (we can always make the GWT compile an optional operation for the all target) I will be happy to add that to the ant script.

--
Pascal Muetschard

Nat Pryce

unread,
Aug 26, 2008, 6:31:20 PM8/26/08
to hamcre...@googlegroups.com
2008/8/26 Pascal Muetschard <pmuet...@google.com>:

> Thank you for your feedback. Here are some clarifications:
> 0. About making the GWT version independent of the Java version...

I'd prefer to share as much as possible, ideally everything, between
the GWT and Java versions.

> 1. About TypeSafeMatcher. Yes, checking for the right type and null is an

> important part of those matchers and is, of course, maintained in GWT...
...


> GWT does not have reflection (or at least only very
> very partial reflection). So, this means that Class.isAssignableFrom and
> Class.isInstance are not available.

That's a pain.

> On the other hand, the "instanceof" operator is.

How strange, since they do the same thing and Javascript is entirely dynamic.

> Now, to use the instanceof operator, we need to know the type
> at compile time. The funny thing is, for all of the library matchers that
> extend TypeSafeMatcher, we do know the type at compile time. So, in order to
> not loose the functionality of all those matchers, I added "intermediate"
> classes, such as StringTypeSafeMatcher, which explicitly state at compile
> time what type they are looking for. This means that when running in plain
> old Java, the TypeSafeMatcher does all the work using reflection (as it does
> now) and the StringTypeSafeMatcher and its cohorts are empty. Whereas for
> GWT, the TypeSafeMatcher is empty and the StringTypeSafeMatcher and its
> cohorts do all the work since at compile time they will use "item instanceof
> <whatever>". I hope that makes more sense.

That's what I'm uncomfortable with. I'd actually prefer that the GWT
and Java versions were the same.

I think that the matchers provided by the Hamcrest should extend
StringTypeSafeMatcher, DoubleTypeSafeMatcher etc. to be GWT-compatible
and TypeSafeMatcher<T> should remain in the library for Java
programmers to extend when writing their own matchers.

(Actually I'd prefer the names StringMatcher, DoubleMatcher, etc.)

> 2. This is a very good question. I have been thinking about this and the
> best solution would be to do a JS compile of the GWT library in ant.
> However, this means adding a dependency in the build libraries on GWT (which
> might not be that bad) and at least an additional 30seconds of build time,
> which sucks since a clean build including running junit currently takes less
> than 10 seconds. If we can live with both (we can always make the GWT
> compile an optional operation for the all target) I will be happy to add
> that to the ant script.

An separate build target that compiles and tests the GWT version would
be great. Adding the GWT libraries as a dependency is not a problem.

--Nat

Steve Freeman

unread,
Aug 26, 2008, 7:02:13 PM8/26/08
to hamcre...@googlegroups.com
I'm also keen to see something happen.

On 26 Aug 2008, at 23:31, Nat Pryce wrote:
> I think that the matchers provided by the Hamcrest should extend
> StringTypeSafeMatcher, DoubleTypeSafeMatcher etc. to be GWT-compatible
> and TypeSafeMatcher<T> should remain in the library for Java
> programmers to extend when writing their own matchers.
>
> (Actually I'd prefer the names StringMatcher, DoubleMatcher, etc.)

So, GWT users might write their own type safe extensions, but not
using the TypeSafeMatcher? We'd need to make clear which parts are GWT-
safe and which not.

>> 2. This is a very good question. I have been thinking about this
>> and the
>> best solution would be to do a JS compile of the GWT library in ant.
>> However, this means adding a dependency in the build libraries on
>> GWT (which
>> might not be that bad) and at least an additional 30seconds of
>> build time,
>> which sucks since a clean build including running junit currently
>> takes less
>> than 10 seconds. If we can live with both (we can always make the GWT
>> compile an optional operation for the all target) I will be happy
>> to add
>> that to the ant script.
>
> An separate build target that compiles and tests the GWT version would
> be great. Adding the GWT libraries as a dependency is not a problem.

Again, we should be clear about what the different environments need
to run. It should be obvious to "normal" users that they don't need
the GWT libraries, so we should make an effort to package nicely.

We should also resolve the difference reporting API before this
happens (or during).

S.

Steve Freeman
Winner of the Agile Alliance Gordon Pask award 2006

http://www.m3p.co.uk

M3P Limited.
Registered office. 2 Church Street, Burnham, Bucks, SL1 7HZ.
Company registered in England & Wales. Number 03689627


Pascal Muetschard

unread,
Aug 26, 2008, 9:02:52 PM8/26/08
to hamcre...@googlegroups.com
> I'd prefer to share as much as possible, ideally everything, between
> the GWT and Java versions.

Agreed. This is the way I did it. The build creates an extra jar file, which people that wish to use Hamcrest in GWT would use along with the regular core and library jars - and if they wish any of the other ones as well, as long as it is only used on server side (i.e. non-JS) parts of their GWT code. When running the GWT app in hosted mode, it will actually use the regular Hamcrest libraries. When the GWT app is compiled for web mode, the classes in the hamcrest-gwt jar will kick in and provide the JS translatable versions of some of the non-translatable Hamcrest classes.

> That's a pain.

Yeah... i know.

>> On the other hand, the "instanceof" operator is.
> How strange, since they do the same thing and Javascript is entirely dynamic.

The main difference being the fact that the class you are testing for is known at compile time for the "instanceof" operator, so all the tests can be examined in the compiler. Since GWT does not support dynamic loading, the compiler will have a full view of the universe and can create lists of classes that should return true for each individual occurrence of "instanceof".


> That's what I'm uncomfortable with.  I'd actually prefer that the GWT
> and Java versions were the same.
>
> I think that the matchers provided by the Hamcrest should extend
> StringTypeSafeMatcher, DoubleTypeSafeMatcher etc. to be GWT-compatible
> and TypeSafeMatcher<T> should remain in the library for Java
> programmers to extend when writing their own matchers.
>
> (Actually I'd prefer the names StringMatcher, DoubleMatcher, etc.)

I have followed this suggestion as you can see by the attached patch. There is now no difference for the library matchers in GWT and regular Java and GWT no longer has "an empty TypeSafeMatcher". At this point there is really very little GWT only code, but there are more changes to the library classes. Please review. I changed the names accordingly as well.


> An separate build target that compiles and tests the GWT version would
> be great.  Adding the GWT libraries as a dependency is not a problem.

I added a test and a validation compile to the ant file's GWT target. I also added a property "nogwt" to the build, if set it will skip the build of the GWT target. The patch does not contain the binary jar files (which is OK, since they make up about 30MB!!) To test you can get them here: http://code.google.com/p/google-web-toolkit/downloads/list (you only need the gwt-user.jar and gwt-dev-<platform>.jar in the lib/gwt/ dir for it to work).


> Again, we should be clear about what the different environments need
> to run. It should be obvious to "normal" users that they don't need
> the GWT libraries, so we should make an effort to package nicely.

The GWT libraries are only required to build the hamcrest-gwt.jar file. They are not at all required for general distributions of Hamcrest and are not part of it either. I also have a README file in the hamcrest-gwt jar which tells the users how to use Hamcrest in GWT. I think it is straight forward and "normal" users should understand what's going on.

--
Pascal Muetschard
patch-gwt-3

Pascal Muetschard

unread,
Aug 29, 2008, 6:57:26 PM8/29/08
to hamcre...@googlegroups.com
Here's a nice coincidence for you: the main reason I started working on getting Hamcrest into GWT was because I wanted to do the same for JMock, which uses Hamcrest. Well, imagine my surprise looking at the JMock project and seeing two familiar names...

Anyways, I have a "working version" of JMock for GWT and I would like you to take a look at it as well. I haven't much tested it yet (especially the code generation part) but I think it looks promising.

There is one issue though. JMock relies heavily on reflection, which as we now well know is not there in GWT. While the mocking is hiding the need for reflection with interfaces (Imposteriser, et al), a lot of the rest of the code depends on java.lang.reflect.Method. I have added a class to replace that one within JMock, put the problem is that there is this one interface org.jmock.syntax.MethodClause that announces to the "outside world" that we are using reflection. In the attached patch, I have changed the method signature of that one method in that one public interface, which means that it could break other people's code... I have not yet found a workaround for this problem and was hoping you guys might help me out here...

It would be great to have both Hamcrest and JMock in GWT, wouldn't you agree? :) I'm certainly hurting for it. Attached are the two patches for Hamcrest and JMock along with a simple GWT test case showing the simple Publisher-Subscriber example and how a JMock unit test would look like in GWT.

I will be gone for the next month with very, very limited access to email, so I appologize in advance for not being able to respond quickly to any questions/feedback you may have.

--
Pascal Muetschard
patch-hc-gwt-3
patch-jmock-gwt-1
PublisherTest.java

Steve Freeman

unread,
Sep 1, 2008, 4:25:03 PM9/1/08
to hamcre...@googlegroups.com, Pascal Muetschard
nice to hear from you.

Nat's away for a couple of weeks, so we can discuss when everyone gets
back.

S

Steve Freeman

Pascal Muetschard

unread,
Oct 2, 2008, 5:48:33 PM10/2/08
to Steve Freeman, hamcre...@googlegroups.com
Alright I'm back. Has anyone had time to review the patches? I would like to get this rolling, starting with the changes to Hamcrest. Does it look OK? What do I need to do to get it in the branch and get it released?

Also, any idea's and feedback about my work on JMock?
--
Pascal Muetschard <pmuet...@google.com>

Nat Pryce

unread,
Oct 12, 2008, 1:51:53 PM10/12/08
to hamcre...@googlegroups.com
Could you raise this as an issue? It's much easier to track what's
being done if it's in the issue tracker than if it's an attachment in
an email.

--Nat

2008/10/2 Pascal Muetschard <pmuet...@google.com>:

Pascal Muetschard

unread,
Oct 21, 2008, 3:15:08 PM10/21/08
to hamcre...@googlegroups.com, Neil Dunn, Paul Hammant
Issue 50 (http://code.google.com/p/hamcrest/issues/detail?id=50) duly raised.
Some additional changes: merged with latest trunk revision (I think 322), gwt source tree is now in the same format as all other source folders.
Please review as soon as you can :).
--
Pascal Muetschard <pmuet...@google.com>
Reply all
Reply to author
Forward
0 new messages