Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 359542

Summary: [XtextTests] Loading a model resource in second test case fails with a "SAXParseException: Content is not allowed in prolog."
Product: [Modeling] TMF Reporter: Alex Tugarev <alex.tugarev>
Component: XtextAssignee: Project Inbox <tmf.xtext-inbox>
Status: CLOSED FIXED QA Contact:
Severity: normal    
Priority: P3 CC: sebastian.zarnekow
Version: 2.0.1Flags: sebastian.zarnekow: indigo+
Target Milestone: SR2   
Hardware: All   
OS: All   
Whiteboard:
Attachments:
Description Flags
Easiest way to reproduce: use the sources from this archived project.
none
Proposal for a bug fix of org.eclipse.xtext.junit4.XtextRunner. none

Description Alex Tugarev CLA 2011-09-30 06:27:27 EDT
Build Identifier: 2.0.1 

Build Identifier: Xtext 2.0.1

Loading a model resource within a second test case will fail because of a "SAXParseException: Content is not allowed in prolog."

In a second test case the resource factory is not registered properly and a XMIResource will be created instead of a XtextResource. 

This bug also directly affects Xtext Unit Testing utilities (http://code.google.com/a/eclipselabs.org/p/xtext-utils/wiki/Unit_Testing).

From debugging I could find out that changes in XtextRunner (since 2.0.0) causing the resource factory is not getting registered properly after evaluation of each test case. 

I'll attach a XtextRunner2 with proposal for a bug fix. See comments within XtextRunner2.java for more details on the bug fix. 

Reproducible: Always

Steps to Reproduce:
1. Create domainmodel example project.
2a. Easiest way: add sources from attached org.eclipse.xtext.example.domainmodel.tests project (zipped archive).
2b. Create at least two test cases. 
3. In second test case load some model from a resource, i.e.

@Test
public void testResourceFactory2() throws Exception {
	URI uri = URI.createURI("classpath:/TestModels/TestModel1.dmodel");
	
	// XMIResource will be created (wrongly)
	Resource resource = resourceSet.createResource(uri);
	try {
		// this throws an exception caused by a
		// "SAXParseException: Content is not allowed in prolog."
		resource.load(null);
		
		// uncomment the line @RunWith(XtextRunner2.class)
		// the XtextRunner2 is fixed, see comments 
	} catch (IOException e) {
		throw new RuntimeException(e);
	}
	EObject eObject = resource.getContents().get(0);
}

4. The test fails because of a SAXParseException as described in the comment.
Comment 1 Alex Tugarev CLA 2011-09-30 06:29:55 EDT
Created attachment 204365 [details]
Easiest way to reproduce: use the sources from this archived project.
Comment 2 Alex Tugarev CLA 2011-09-30 06:31:14 EDT
Created attachment 204366 [details]
Proposal for a bug fix of org.eclipse.xtext.junit4.XtextRunner.
Comment 3 Sebastian Zarnekow CLA 2011-09-30 07:11:51 EDT
I'd reject the suggested change since it is important to store the old registry values in #setupRegistry. However, the standalone setup has to be used to call #register(Injector) for each methodBlock and not only for the first one. Therefore we may have to change the interface of ISetup or we need to provide an additional interface ISetupExtension that's implemented by the generated StandaloneSetups.
Comment 4 Alex Tugarev CLA 2011-09-30 07:43:30 EDT
(In reply to comment #3)
> I'd reject the suggested change since it is important to store the old registry
> values in #setupRegistry.

Sebastian, maybe I didn't understand your comment but I did not disable the call of #setupRegistry. As I could see from debuggin the XtextRunner#methodBlock of 2.0.0 calls registryConfigurator.setupRegistry() and super.methodBlock(method) in different order and the second one calls <DSL>InjectorProvider.getInjector(). This difference leads to unregistered resource factory. 

> However, the standalone setup has to be used to call
> #register(Injector) for each methodBlock and not only for the first one.
> Therefore we may have to change the interface of ISetup or we need to provide
> an additional interface ISetupExtension that's implemented by the generated
> StandaloneSetups.

I'm interested in a proper solution and more details on this.
Comment 5 Sebastian Zarnekow CLA 2011-09-30 07:45:39 EDT
(In reply to comment #4)
> (In reply to comment #3)
> > I'd reject the suggested change since it is important to store the old registry
> > values in #setupRegistry.
> 
> Sebastian, maybe I didn't understand your comment but I did not disable the
> call of #setupRegistry.

Sorry, my comment was incomplete:
It is important to store the old registry contents prior to invoking the method block. Otherwise it would not be possible to restore the original contents and thereby revert any side-effects that were caused by the method block itself.
Comment 6 Sebastian Zarnekow CLA 2011-10-13 16:27:23 EDT
I created a new project and used this testcase:

@RunWith(XtextRunner.class)
@InjectWith(MyDslInjectorProvider.class)
public class SampleTest {

	@Inject
	XtextResourceSet resourceSet;
	
	@Test
	public void testResourceFactory() throws Exception {
	    URI uri = URI.createURI("sample.mydsl");

	    // XMIResource will be created (wrongly)
	    Resource resource = resourceSet.createResource(uri);
	    Assert.assertTrue(resource instanceof XtextResource);
	}
	@Test
	public void testResourceFactory2() throws Exception {
		URI uri = URI.createURI("sample.mydsl");
		
		// XMIResource will be created (wrongly)
		Resource resource = resourceSet.createResource(uri);
		Assert.assertTrue(resource instanceof XtextResource);
	}
	
}

Everything worked fine so far. Please reopen if I missed something.
Comment 7 Alex Tugarev CLA 2011-10-14 06:32:53 EDT
(In reply to comment #6)
Now I've an idea what's wrong with the <Dsl>InjectorProvider. The bug is reproducible if your #setupRegistry() in <Dsl>InjectorProvider looks like this:

	public void setupRegistry() {
		globalStateMemento = GlobalRegistries.makeCopyOfGlobalState();
	}

As it worked fine for you, I was wondering what could be the difference. I spent some time on this trying to understand the <Dsl>InjectorProvider after I read your #c5. And my idea was to change #setupRegistry() in <Dsl>InjectorProvider to:

	public void setupRegistry() {
		globalStateMemento = GlobalRegistries.makeCopyOfGlobalState();
		if (injector != null) 
			new <Dsl>StandaloneSetup().register(injector);
	}

After your last comment I tried the last nightly build Xtext-N201110132135 to see how the generated <Dsl>InjectorProvider might have changed. It looks like this:

	public void setupRegistry() {
		globalStateMemento = GlobalRegistries.makeCopyOfGlobalState();
		if (injector != null)
			new MyDslStandaloneSetup().register(injector);
	}

And this realy works fine!

I've just double-tested the 2.0.1 (from Xtext-Disto) and the bug is still there. The generated <Dsl>InjectorProvider#setupRegistry() looks like the first one.

Does this bug need to be reopened?
Comment 8 Sebastian Zarnekow CLA 2011-10-14 07:09:11 EDT
(In reply to comment #7)
> Does this bug need to be reopened?

No, since it's fixed for 2.1 and we don't plan a 2.0.2
Comment 9 Karsten Thoms CLA 2017-09-19 17:11:57 EDT
Closing all bugs that were set to RESOLVED before Neon.0
Comment 10 Karsten Thoms CLA 2017-09-19 17:23:30 EDT
Closing all bugs that were set to RESOLVED before Neon.0