Search code examples
junit4xtextxtend

xtext parameterized xtext runner


Purpose: Run parameterized tests within xtext/xtend context.

Progress: So far I have achieved getting it to run, but it is appearing wrong in the junit window.

Issues: The failure trace and results of both tests appear in the last test, as shown in the figure below. The first test, marked by the red pen, is sort of unresolved and does not contain any failure trace.

junit window

Here is the test class:

@RunWith(typeof(Parameterized))
@InjectWith(SemanticAdaptationInjectorProvider)
@Parameterized.UseParametersRunnerFactory(XtextParametersRunnerFactory)
class CgCppAutoTest extends AbstractSemanticAdaptationTest {

    new (List<File> files)
    {
        f = files;
    }

    @Inject extension ParseHelper<SemanticAdaptation>
    @Inject extension  ValidationTestHelper

    @Parameters(name = "{index}")
    def static Collection<Object[]> data() {
        val files = new ArrayList<List<File>>();
        listf("test_input", files);
        val test = new ArrayList();
        test.add(files.get(0));
        return Arrays.asList(test.toArray(), test.toArray());
    }

    def static void listf(String directoryName, List<List<File>> files) {
        ...
    }

    var List<File> f;

    @Test def allSemanticAdaptations() {
        System.out.println("fail");
        assertTrue(false);
    }
}

ParameterizedXtextRunner (Inspiration from here: https://www.eclipse.org/forums/index.php?t=msg&th=1075706&goto=1726802&):

class ParameterizedXtextRunner extends XtextRunner {
    val Object[] parameters;
    val String annotatedName;
    new(TestWithParameters test) throws InitializationError {
        super(test.testClass.javaClass)
        parameters = test.parameters;
        annotatedName = test.name;
    }
    override protected getName() {
        return super.name + annotatedName;
    }
    override protected createTest() throws Exception {
        val object = testClass.onlyConstructor.newInstance(parameters)
        val injectorProvider = getOrCreateInjectorProvider
        if (injectorProvider != null) {
            val injector = injectorProvider.injector
            if (injector != null)
                injector.injectMembers(object)
        }
        return object;
    }
    override protected void validateConstructor(List<Throwable> errors) {
        validateOnlyOneConstructor(errors)
    }

And finally XtextParametersRunnerFactory:

class XtextParametersRunnerFactory implements ParametersRunnerFactory {
    override createRunnerForTestWithParameters(TestWithParameters test) throws InitializationError {
        new ParameterizedXtextRunner(test)
    }
}

Solution

  • By looking at the XtextRunner class it inherits from BlockJUnit4ClassRunner. Parameterized does not extend this runner, but ParentRunner. However, so does BlockJUnit4ClassRunner Therefore we implemented it as below:

    public class XtextParametersRunnerFactory implements ParametersRunnerFactory {
        @Override
        public Runner createRunnerForTestWithParameters(TestWithParameters test) throws InitializationError {
            return new XtextRunnerWithParameters(test);
        }
    }
    

    And used the code from XtextRunner and put it into the new runner -- it is necessary to extract InjectorProviders from Xtext as well

    public class XtextRunnerWithParameters extends BlockJUnit4ClassRunnerWithParameters {
        public XtextRunnerWithParameters(TestWithParameters test) throws InitializationError {
            super(test);
        }
    
        @Override
        public Object createTest() throws Exception {
            Object object = super.createTest();
            IInjectorProvider injectorProvider = getOrCreateInjectorProvider();
            if (injectorProvider != null) {
                Injector injector = injectorProvider.getInjector();
                if (injector != null)
                    injector.injectMembers(object);
            }
            return object;
        }
    
        @Override
        protected Statement methodBlock(FrameworkMethod method) {
            IInjectorProvider injectorProvider = getOrCreateInjectorProvider();
            if (injectorProvider instanceof IRegistryConfigurator) {
                final IRegistryConfigurator registryConfigurator = (IRegistryConfigurator) injectorProvider;
                registryConfigurator.setupRegistry();
                final Statement methodBlock = superMethodBlock(method);
                return new Statement() {
                    @Override
                    public void evaluate() throws Throwable {
                        try {
                            methodBlock.evaluate();
                        } finally {
                            registryConfigurator.restoreRegistry();
                        }
                    }
                };
            }else{
                return superMethodBlock(method);
            }
        }
    
        protected Statement superMethodBlock(FrameworkMethod method) {
            return super.methodBlock(method);
        }
    
        protected IInjectorProvider getOrCreateInjectorProvider() {
            return InjectorProviders.getOrCreateInjectorProvider(getTestClass());
        }
    
        protected IInjectorProvider getInjectorProvider() {
            return InjectorProviders.getInjectorProvider(getTestClass());
        }
    
        protected IInjectorProvider createInjectorProvider() {
            return InjectorProviders.createInjectorProvider(getTestClass());
        }
    
    }
    

    Creating a test:

    @RunWith(typeof(Parameterized))
    @InjectWith(SemanticAdaptationInjectorProvider)
    @Parameterized.UseParametersRunnerFactory(XtextParametersRunnerFactory)
    class xxx
    {
        @Inject extension ParseHelper<SemanticAdaptation>
        @Inject extension  ValidationTestHelper
     // Here goes standard parameterized stuff
    
    }