Cleaning up project structure.
diff --git a/com.google.eclipse.protobuf.test/META-INF/MANIFEST.MF b/com.google.eclipse.protobuf.test/META-INF/MANIFEST.MF
index 646fe3f..a9b6101 100644
--- a/com.google.eclipse.protobuf.test/META-INF/MANIFEST.MF
+++ b/com.google.eclipse.protobuf.test/META-INF/MANIFEST.MF
@@ -9,6 +9,10 @@
  org.junit.source;bundle-version="4.8.1",
  org.eclipse.xtext.junit;bundle-version="2.0.0",
  org.eclipse.xtext.junit4;bundle-version="2.0.0",
- com.google.eclipse.protobuf.junit;bundle-version="1.0.0",
- org.mockito;bundle-version="1.8.5"
+ org.mockito;bundle-version="1.8.5",
+ org.eclipse.core.resources;bundle-version="3.7.100"
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Export-Package: com.google.eclipse.protobuf.junit.core,
+ com.google.eclipse.protobuf.junit.matchers,
+ com.google.eclipse.protobuf.junit.stubs,
+ com.google.eclipse.protobuf.junit.util
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/XtextRule.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/XtextRule.java
new file mode 100644
index 0000000..b130379
--- /dev/null
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/XtextRule.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.junit.core;
+
+import static org.eclipse.emf.common.util.URI.createURI;
+import static org.eclipse.emf.ecore.util.EcoreUtil.resolveAll;
+import static org.eclipse.xtext.util.CancelIndicator.NullImpl;
+
+import java.io.*;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.xtext.linking.lazy.LazyLinkingResource;
+import org.eclipse.xtext.resource.*;
+import org.eclipse.xtext.util.StringInputStream;
+import org.junit.rules.MethodRule;
+import org.junit.runners.model.*;
+
+import com.google.eclipse.protobuf.ProtobufStandaloneSetup;
+import com.google.eclipse.protobuf.protobuf.Protobuf;
+import com.google.inject.Injector;
+
+/**
+ * Rule that performs configuration of a standalone Xtext environment.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class XtextRule implements MethodRule {
+
+  private Injector injector;
+
+  public Statement apply(Statement base, FrameworkMethod method, Object target) {
+    return new XtextStatement(base);
+  }
+
+  public Injector injector() {
+    return injector;
+  }
+
+  public Protobuf parse(StringBuilder text) {
+    return parse(text.toString());
+  }
+
+  public Protobuf parse(String text) {
+    XtextResource resource = resourceFrom(new StringInputStream(text));
+    return (Protobuf) resource.getParseResult().getRootASTElement();
+  }
+
+  private XtextResource resourceFrom(InputStream input) {
+    return resourceFrom(input, createURI("mytestmodel.proto")); //$NON-NLS-1$
+  }
+
+  private XtextResource resourceFrom(InputStream input, URI uri) {
+    XtextResourceSet set = getInstanceOf(XtextResourceSet.class);
+    set.setClasspathURIContext(getClass());
+    XtextResource resource = (XtextResource) getInstanceOf(IResourceFactory.class).createResource(uri);
+    set.getResources().add(resource);
+    try {
+      resource.load(input, null);
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+    if (resource instanceof LazyLinkingResource) {
+      ((LazyLinkingResource) resource).resolveLazyCrossReferences(NullImpl);
+      return resource;
+    }
+    resolveAll(resource);
+    return resource;
+  }
+
+  public <T> T getInstanceOf(Class<T> type) {
+    return injector.getInstance(type);
+  }
+
+  private class XtextStatement extends Statement {
+    private final Statement base;
+
+    public XtextStatement(Statement base) {
+      this.base = base;
+    }
+
+    @Override public void evaluate() throws Throwable {
+      setUpInjector();
+      base.evaluate();
+    }
+
+    private void setUpInjector() {
+      injector = new ProtobufStandaloneSetup().createInjectorAndDoEMFRegistration();
+    }
+  }
+}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/matchers/EnumHasLiterals.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/matchers/EnumHasLiterals.java
new file mode 100644
index 0000000..57cd397
--- /dev/null
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/matchers/EnumHasLiterals.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.junit.matchers;
+
+import java.util.*;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+
+import com.google.eclipse.protobuf.protobuf.Enum;
+import com.google.eclipse.protobuf.protobuf.Literal;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class EnumHasLiterals extends BaseMatcher<Enum> {
+
+  private final String[] literalNames;
+
+  public static EnumHasLiterals hasLiterals(String... literalNames) {
+    return new EnumHasLiterals(literalNames);
+  }
+  
+  private EnumHasLiterals(String... literalNames) {
+    this.literalNames = literalNames;
+  }
+  
+  /** {@inheritDoc} */
+  public boolean matches(Object arg) {
+    if (!(arg instanceof Enum)) return false;
+    Enum anEnum = (Enum) arg;
+    List<String> actualNames = literalNames(anEnum);
+    for (String name : literalNames) actualNames.remove(name);
+    return actualNames.isEmpty();
+  }
+
+  private List<String> literalNames(Enum anEnum) {
+    List<String> names = new ArrayList<String>();
+    for (Literal literal : anEnum.getLiterals()) names.add(literal.getName());
+    return names;
+  }
+  
+  /** {@inheritDoc} */
+  public void describeTo(Description description) {
+    description.appendValue(Arrays.toString(literalNames));
+  }
+}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/matchers/PropertyHasType.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/matchers/PropertyHasType.java
new file mode 100644
index 0000000..1a4464b
--- /dev/null
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/matchers/PropertyHasType.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.junit.matchers;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+
+import com.google.eclipse.protobuf.protobuf.*;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class PropertyHasType extends BaseMatcher<Property> {
+
+  private final String typeName;
+
+  public static PropertyHasType hasType(String typeName) {
+    return new PropertyHasType(typeName);
+  }
+  
+  private PropertyHasType(String typeName) {
+    this.typeName = typeName;
+  }
+  
+  /** {@inheritDoc} */
+  public boolean matches(Object arg) {
+    if (!(arg instanceof Property)) return false;
+    Property property = (Property) arg;
+    return typeName.equals(typeNameOf(property));
+  }
+  
+  private String typeNameOf(Property property) {
+    AbstractTypeReference r = property.getType();
+    if (r instanceof ScalarTypeReference) return ((ScalarTypeReference) r).getScalar().getName();
+    if (r instanceof TypeReference) {
+      Type type = ((TypeReference) r).getType();
+      return type == null ? null : type.getName();
+    }
+    return r.toString();
+  }
+
+  /** {@inheritDoc} */
+  public void describeTo(Description description) {
+    description.appendValue(typeName);
+  }
+}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/stubs/FileStub.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/stubs/FileStub.java
new file mode 100644
index 0000000..d3b036d
--- /dev/null
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/stubs/FileStub.java
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.junit.stubs;
+
+import static java.util.Collections.*;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.util.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class FileStub implements IFile {
+
+  private final Map<String, List<MarkerStub>> markersByType = new HashMap<String, List<MarkerStub>>();
+
+  /** {@inheritDoc} */
+  public void accept(IResourceProxyVisitor visitor, int memberFlags) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void accept(IResourceVisitor visitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void accept(IResourceVisitor visitor, int depth, boolean includePhantoms) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void accept(IResourceVisitor visitor, int depth, int memberFlags) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void appendContents(InputStream source, boolean force, boolean keepHistory, IProgressMonitor monitor)
+      {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void appendContents(InputStream source, int updateFlags, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void clearHistory(IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean contains(ISchedulingRule rule) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void copy(IPath destination, boolean force, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void copy(IPath destination, int updateFlags, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void copy(IProjectDescription description, boolean force, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void copy(IProjectDescription description, int updateFlags, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void create(InputStream source, boolean force, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void create(InputStream source, int updateFlags, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void createLink(IPath localLocation, int updateFlags, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void createLink(URI location, int updateFlags, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IMarker createMarker(String type) {
+    MarkerStub marker = new MarkerStub(type);
+    addMarker(marker);
+    return marker;
+  }
+
+  /** {@inheritDoc} */
+  public IResourceProxy createProxy() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void delete(boolean force, boolean keepHistory, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void delete(boolean force, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void delete(int updateFlags, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void deleteMarkers(String type, boolean includeSubtypes, int depth) {
+    List<MarkerStub> markers = markersByType.get(type);
+    if (markers != null) markers.clear();
+  }
+
+  /** {@inheritDoc} */
+  public boolean exists() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IMarker findMarker(long id) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IMarker[] findMarkers(String type, boolean includeSubtypes, int depth) {
+    List<MarkerStub> markers = markersByType.get(type);
+    if (markers == null) return new IMarker[0];
+    return markers.toArray(new IMarker[markers.size()]);
+  }
+
+  /** {@inheritDoc} */
+  public int findMaxProblemSeverity(String type, boolean includeSubtypes, int depth) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  @SuppressWarnings("rawtypes") public Object getAdapter(Class adapter) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public String getCharset() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public String getCharset(boolean checkImplicit) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public String getCharsetFor(Reader reader) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IContentDescription getContentDescription() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public InputStream getContents() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public InputStream getContents(boolean force) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  @Deprecated public int getEncoding() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public String getFileExtension() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IPath getFullPath() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IFileState[] getHistory(IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public long getLocalTimeStamp() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IPath getLocation() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public URI getLocationURI() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IMarker getMarker(long id) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public long getModificationStamp() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public String getName() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IContainer getParent() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IPathVariableManager getPathVariableManager() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public Map<QualifiedName, String> getPersistentProperties() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public String getPersistentProperty(QualifiedName key) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IProject getProject() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IPath getProjectRelativePath() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IPath getRawLocation() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public URI getRawLocationURI() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public ResourceAttributes getResourceAttributes() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public Map<QualifiedName, Object> getSessionProperties() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public Object getSessionProperty(QualifiedName key) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public int getType() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IWorkspace getWorkspace() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isAccessible() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isConflicting(ISchedulingRule rule) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isDerived() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isDerived(int options) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isHidden() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isHidden(int options) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isLinked() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isLinked(int options) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  @Deprecated public boolean isLocal(int depth) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isPhantom() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isReadOnly() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isSynchronized(int depth) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isTeamPrivateMember() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isTeamPrivateMember(int options) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isVirtual() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void move(IPath destination, boolean force, boolean keepHistory, IProgressMonitor monitor)
+      {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void move(IPath destination, boolean force, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void move(IPath destination, int updateFlags, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void move(IProjectDescription description, boolean force, boolean keepHistory, IProgressMonitor monitor)
+      {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void move(IProjectDescription description, int updateFlags, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void refreshLocal(int depth, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void revertModificationStamp(long value) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  @Deprecated public void setCharset(String newCharset) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setCharset(String newCharset, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setContents(IFileState source, boolean force, boolean keepHistory, IProgressMonitor monitor)
+      {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setContents(IFileState source, int updateFlags, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setContents(InputStream source, boolean force, boolean keepHistory, IProgressMonitor monitor)
+      {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setContents(InputStream source, int updateFlags, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  @Deprecated public void setDerived(boolean isDerived) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setDerived(boolean isDerived, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setHidden(boolean isHidden) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  @Deprecated public void setLocal(boolean flag, int depth, IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public long setLocalTimeStamp(long value) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setPersistentProperty(QualifiedName key, String value) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  @Deprecated public void setReadOnly(boolean readOnly) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setResourceAttributes(ResourceAttributes attributes) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setSessionProperty(QualifiedName key, Object value) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setTeamPrivateMember(boolean isTeamPrivate) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void touch(IProgressMonitor monitor) {
+    throw new UnsupportedOperationException();
+  }
+
+  public int markerCount(String type) {
+    List<MarkerStub> markers = markersByType.get(type);
+    return (markers == null) ? 0 : markers.size();
+  }
+  
+  public List<MarkerStub> markers(String type) {
+    List<MarkerStub> markers = markersByType.get(type);
+    if (markers == null) return emptyList();
+    return unmodifiableList(markers);
+  }
+
+  public void addMarker(MarkerStub marker) {
+    String type = marker.getType();
+    List<MarkerStub> markers = markersByType.get(type);
+    if (markers == null) {
+      markers = new ArrayList<MarkerStub>();
+      markersByType.put(type, markers);
+    }
+    markers.add(marker);
+  }
+}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/stubs/MarkerStub.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/stubs/MarkerStub.java
new file mode 100644
index 0000000..8386a8a
--- /dev/null
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/stubs/MarkerStub.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.junit.stubs;
+
+import static java.util.Collections.unmodifiableMap;
+
+import java.util.*;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class MarkerStub implements IMarker {
+
+  private final Map<String, Object> attributes = new HashMap<String, Object>();
+  private final String type;
+  private final long creationTime;
+
+  public static MarkerStub error(String type, String description, int lineNumber) {
+    MarkerStub marker = new MarkerStub(type);
+    marker.setAttribute(SEVERITY, SEVERITY_ERROR);
+    marker.setAttribute(MESSAGE, description);
+    marker.setAttribute(LINE_NUMBER, lineNumber);
+    return marker;
+  }
+
+  public MarkerStub(String type) {
+    this.type = type;
+    creationTime = System.currentTimeMillis();
+  }
+
+  /** {@inheritDoc} */
+  @SuppressWarnings("rawtypes") public Object getAdapter(Class adapter) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void delete() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean exists() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public Object getAttribute(String attributeName) {
+    return attributes.get(attributeName);
+  }
+
+  /** {@inheritDoc} */
+  public int getAttribute(String attributeName, int defaultValue) {
+    Object attribute = attributes.get(attributeName);
+    if (attribute instanceof Integer) return (Integer) attribute;
+    return defaultValue;
+  }
+
+  /** {@inheritDoc} */
+  public String getAttribute(String attributeName, String defaultValue) {
+    Object attribute = attributes.get(attributeName);
+    if (attribute instanceof String) return (String) attribute;
+    return defaultValue;
+  }
+
+  /** {@inheritDoc} */
+  public boolean getAttribute(String attributeName, boolean defaultValue) {
+    Object attribute = attributes.get(attributeName);
+    if (attribute instanceof Boolean) return (Boolean) attribute;
+    return defaultValue;
+  }
+
+  /** {@inheritDoc} */
+  public Map<String, Object> getAttributes() {
+    return unmodifiableMap(attributes);
+  }
+
+  /** {@inheritDoc} */
+  public Object[] getAttributes(String[] attributeNames) {
+    List<Object> values = new ArrayList<Object>();
+    for (String name : attributeNames)
+      values.add(attributes.get(name));
+    return values.toArray();
+  }
+
+  /** {@inheritDoc} */
+  public long getCreationTime() {
+    return creationTime;
+  }
+
+  /** {@inheritDoc} */
+  public long getId() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public IResource getResource() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public String getType() {
+    return type;
+  }
+
+  /** {@inheritDoc} */
+  public boolean isSubtypeOf(String superType) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setAttribute(String attributeName, int value) {
+    attributes.put(attributeName, value);
+  }
+
+  /** {@inheritDoc} */
+  public void setAttribute(String attributeName, Object value) {
+    attributes.put(attributeName, value);
+  }
+
+  /** {@inheritDoc} */
+  public void setAttribute(String attributeName, boolean value) {
+    attributes.put(attributeName, value);
+  }
+
+  /** {@inheritDoc} */
+  public void setAttributes(String[] attributeNames, Object[] values) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setAttributes(Map<String, ? extends Object> attributes) {
+    this.attributes.putAll(attributes);
+  }
+
+  public int severity() {
+    return getAttribute(SEVERITY, -1);
+  }
+  
+  public String message() {
+    return (String) getAttribute(MESSAGE);
+  }
+
+  public int lineNumber() {
+    return getAttribute(LINE_NUMBER, -1);
+  }
+
+  @Override public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (obj == null) return false;
+    if (getClass() != obj.getClass()) return false;
+    MarkerStub other = (MarkerStub) obj;
+    if (attributes == null) {
+      if (other.attributes != null) return false;
+    } else if (!attributes.equals(other.attributes)) return false;
+    if (type == null) {
+      if (other.type != null) return false;
+    } else if (!type.equals(other.type)) return false;
+    return true;
+  }
+
+  @Override public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((attributes == null) ? 0 : attributes.hashCode());
+    result = prime * result + ((type == null) ? 0 : type.hashCode());
+    return result;
+  }
+
+  @Override public String toString() {
+    return "MarkerStub [attributes=" + attributes + "]";
+  }
+}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/stubs/ResourceStub.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/stubs/ResourceStub.java
new file mode 100644
index 0000000..3da6b23
--- /dev/null
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/stubs/ResourceStub.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.junit.stubs;
+
+import static org.eclipse.emf.common.util.URI.createURI;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+
+import org.eclipse.emf.common.notify.Adapter;
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.common.util.*;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ResourceStub implements Resource {
+
+  private URI uri;
+
+  public ResourceStub() {}
+  
+  public ResourceStub(String uri) {
+    setURI(createURI(uri));
+  }
+  
+  /** {@inheritDoc} */
+  public EList<Adapter> eAdapters() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean eDeliver() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void eSetDeliver(boolean deliver) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void eNotify(Notification notification) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public ResourceSet getResourceSet() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public URI getURI() {
+    return uri;
+  }
+
+  /** {@inheritDoc} */
+  public void setURI(URI uri) {
+    this.uri = uri;
+  }
+
+  /** {@inheritDoc} */
+  public long getTimeStamp() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setTimeStamp(long timeStamp) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public EList<EObject> getContents() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public TreeIterator<EObject> getAllContents() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public String getURIFragment(EObject eObject) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public EObject getEObject(String uriFragment) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void save(Map<?, ?> options) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void load(Map<?, ?> options) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void save(OutputStream outputStream, Map<?, ?> options) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void load(InputStream inputStream, Map<?, ?> options) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isTrackingModification() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setTrackingModification(boolean isTrackingModification) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isModified() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void setModified(boolean isModified) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public boolean isLoaded() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void unload() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public void delete(Map<?, ?> options) {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public EList<Diagnostic> getErrors() {
+    throw new UnsupportedOperationException();
+  }
+
+  /** {@inheritDoc} */
+  public EList<Diagnostic> getWarnings() {
+    throw new UnsupportedOperationException();
+  }
+
+}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/util/Finder.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/util/Finder.java
new file mode 100644
index 0000000..31583dc
--- /dev/null
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/util/Finder.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.junit.util;
+
+import static org.eclipse.xtext.EcoreUtil2.getAllContentsOfType;
+
+import java.util.List;
+
+import com.google.eclipse.protobuf.protobuf.*;
+import com.google.eclipse.protobuf.protobuf.Enum;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public final class Finder {
+
+  public static Enum findEnum(String name, Protobuf root) {
+    for (Enum anEnum : getAllContentsOfType(root, Enum.class))
+      if (name.equals(anEnum.getName())) return anEnum;
+    return null;
+  }
+
+  public static Message findMessage(String name, Protobuf root) {
+    for (Message message : getAllContentsOfType(root, Message.class))
+      if (name.equals(message.getName())) return message;
+    return null;
+  }
+
+  public static Option findOption(String name, Protobuf root) {
+    for (Option option : root.getOptions())
+      if (name.equals(option.getName())) return option;
+    return null;
+  }
+
+  public static Property findProperty(String name, Protobuf root) {
+    for (Property property : allProperties(root))
+      if (name.equals(property.getName())) return property;
+    return null;
+  }
+
+  public static List<Property> allProperties(Protobuf root) {
+    return getAllContentsOfType(root, Property.class);
+  }
+
+  public static Literal findLiteral(String name, Protobuf root) {
+    List<Literal> literals = getAllContentsOfType(root, Literal.class);
+    for (Literal literal : literals)
+      if (name.equals(literal.getName())) return literal;
+        return null;
+  }
+
+  private Finder() {}
+}
diff --git a/com.google.eclipse.protobuf.ui.test/.classpath b/com.google.eclipse.protobuf.ui.test/.classpath
index 64c5e31..1013bd4 100644
--- a/com.google.eclipse.protobuf.ui.test/.classpath
+++ b/com.google.eclipse.protobuf.ui.test/.classpath
@@ -3,5 +3,6 @@
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
 	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
 	<classpathentry kind="src" path="src"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/com.google.eclipse.protobuf.test"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/com.google.eclipse.protobuf.ui.test/META-INF/MANIFEST.MF b/com.google.eclipse.protobuf.ui.test/META-INF/MANIFEST.MF
index 464e915..5484ca8 100644
--- a/com.google.eclipse.protobuf.ui.test/META-INF/MANIFEST.MF
+++ b/com.google.eclipse.protobuf.ui.test/META-INF/MANIFEST.MF
@@ -11,5 +11,4 @@
  org.eclipse.xtext.junit;bundle-version="2.0.0",
  org.eclipse.xtext.junit4;bundle-version="2.0.0",
  org.eclipse.xtext.ui.junit;bundle-version="2.0.0",
- com.google.eclipse.protobuf.junit;bundle-version="1.0.0",
  org.mockito;bundle-version="1.8.5"
diff --git a/com.google.eclipse.protobuf/META-INF/MANIFEST.MF b/com.google.eclipse.protobuf/META-INF/MANIFEST.MF
index cbe1187..a7191ae 100644
--- a/com.google.eclipse.protobuf/META-INF/MANIFEST.MF
+++ b/com.google.eclipse.protobuf/META-INF/MANIFEST.MF
@@ -1,5 +1,6 @@
 Manifest-Version: 1.0

 Bundle-ManifestVersion: 2

+Eclipse-ExtensibleAPI: true

 Bundle-Name: %Bundle-Name

 Bundle-Vendor: %Bundle-Vendor

 Bundle-Version: 1.0.0