Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion com.avaloq.tools.ddk.xtext.generator.test/.classpath
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?><classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="xtend-gen"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
Expand Down
6 changes: 0 additions & 6 deletions com.avaloq.tools.ddk.xtext.generator.test/.project
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
<arguments>
Expand All @@ -44,7 +39,6 @@
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
<nature>net.sourceforge.pmd.eclipse.plugin.pmdNature</nature>
<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
<nature>edu.umd.cs.findbugs.plugin.eclipse.findbugsNature</nature>
Expand Down
3 changes: 1 addition & 2 deletions com.avaloq.tools.ddk.xtext.generator.test/build.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
source.. = src/,\
xtend-gen/
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
/*******************************************************************************
* Copyright (c) 2016 Avaloq Group AG and others.
* 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
*
* Contributors:
* Avaloq Group AG - initial API and implementation
*******************************************************************************/

package com.avaloq.tools.ddk.xtext.generator.xbase.test;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.Arrays;
import java.util.Iterator;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.AbstractMetamodelDeclaration;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.Group;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.TypeRef;
import org.eclipse.xtext.XtextPackage;
import org.eclipse.xtext.testing.extensions.InjectionExtension;
import org.eclipse.xtext.xtext.generator.xbase.XbaseUsageDetector;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;

import com.avaloq.tools.ddk.test.core.jupiter.BugTest;
import com.avaloq.tools.ddk.test.core.jupiter.BugTestAwareRule;


/**
* Tests for {@link XbaseUsageDetector}.
*/
@ExtendWith(InjectionExtension.class)
@SuppressWarnings("nls")
public class XbaseGeneratorFragmentTest {

/** A leaf rule's (nullable) metamodel package name and its parser-rule name. */
private record PackageAndRuleName(String packageName, String ruleName) { }

// CHECKSTYLE:CONSTANTS-OFF
@RegisterExtension
// CHECKSTYLE:CHECK-OFF Visibility MethodRules cannot be private
public BugTestAwareRule bugTestRule = BugTestAwareRule.getInstance();
// CHECKSTYLE:CHECK-ON Visibility

private static final String THIS_PACKAGE_NAME = "thisPackage";
private static final String XTYPE_PACKAGE_NAME = "xtype";
private static final String X_IMPORT_SECTION_RULE_NAME = "XImportSection";

private final XbaseUsageDetector detector = new XbaseUsageDetector();

// Shared EClass instance used as the resolved Xtype::XImportSection. EcoreUtil2.isAssignableFrom
// returns true via reference equality when the rule's type classifier is this same instance.
private final EClass mockXtypeXImportSectionEClass = mock(EClass.class);
private final ResourceSet mockResourceSet = mock(ResourceSet.class);
private final Resource mockResource = mock(Resource.class);

@BeforeEach
void setUp() {
when(mockResourceSet.getEObject(any(URI.class), eq(true))).thenReturn(mockXtypeXImportSectionEClass);
when(mockResource.getResourceSet()).thenReturn(mockResourceSet);
}

/**
* Set expectations prior to calling usesXImportSection.apply().
*
* @param mockRule
* Mock Rule, in which to set expectations.
* @param packageName
* Package name to use.
* @param ruleName
* Rule name to use.
*/
private void setExpectationsForApply(final ParserRule mockRule, final String packageName, final String ruleName) {
final TypeRef mockType = mock(TypeRef.class);
final AbstractMetamodelDeclaration mockMetamodel = mock(AbstractMetamodelDeclaration.class);
final EPackage mockEPackage = mock(EPackage.class);

when(mockRule.getName()).thenReturn(ruleName);

when(mockRule.getType()).thenReturn(mockType);
when(mockType.getMetamodel()).thenReturn(mockMetamodel);
when(mockMetamodel.getEPackage()).thenReturn(mockEPackage);
when(mockEPackage.getName()).thenReturn(packageName);

// The current XbaseUsageDetector navigates rule.eResource().getResourceSet() to look up the
// Xtype XImportSection EClass, then compares it (via EcoreUtil2.isAssignableFrom) against the
// rule's type classifier. Treat (xtypePackageName + XImportSection) as the canonical match
// by giving such rules the same EClass instance the resource-set lookup returns; everything
// else gets a fresh distinct mock so isAssignableFrom evaluates false.
when(mockRule.eResource()).thenReturn(mockResource);
final EClass classifier = XTYPE_PACKAGE_NAME.equals(packageName) && X_IMPORT_SECTION_RULE_NAME.equals(ruleName)
? mockXtypeXImportSectionEClass
: mock(EClass.class);
when(mockType.getClassifier()).thenReturn(classifier);
}

/**
* Set expectations prior to calling usesXImportSection().
*
* @param mockGrammar
* Mock Grammar, in which to set expectations.
* @param packageAndRuleNamesOfLeafRules
* Package and rule names to use for leaf rules.
*/
private void setExpectationsForUsesXImportSection(final Grammar mockGrammar, final PackageAndRuleName... packageAndRuleNamesOfLeafRules) {
final ParserRule mockRootRule = mock(ParserRule.class);
final Group mockAlternatives = mock(Group.class);
final EList<AbstractElement> mockElements = new BasicEList<>();

final EList<ParserRule> mockLeafRules = new BasicEList<>();
for (int i = 0; i < packageAndRuleNamesOfLeafRules.length; i++) {
mockLeafRules.add(mock(ParserRule.class));
}

final EList<AbstractRule> mockRules = new BasicEList<>();
mockRules.add(mockRootRule);
mockRules.addAll(mockLeafRules);

// Calls made by UsedRulesFinder.compute()
when(mockGrammar.getRules()).thenReturn(mockRules);

// Calls made by doSwitch(firstRule)
when(mockRootRule.eClass()).thenReturn(XtextPackage.Literals.PARSER_RULE);
when(mockRootRule.getAlternatives()).thenReturn(mockAlternatives);

when(mockAlternatives.eClass()).thenReturn(XtextPackage.Literals.GROUP);
when(mockAlternatives.getElements()).thenReturn(mockElements);

// Calls made per leaf rule by doSwitch(firstRule)
for (final ParserRule mockLeafRule : mockLeafRules) {
final Assignment mockAssignment = mock(Assignment.class);
final RuleCall mockTerminal = mock(RuleCall.class);
mockElements.add(mockAssignment);

when(mockAssignment.eClass()).thenReturn(XtextPackage.Literals.ASSIGNMENT);
when(mockAssignment.getTerminal()).thenReturn(mockTerminal);

when(mockTerminal.eClass()).thenReturn(XtextPackage.Literals.RULE_CALL);
when(mockTerminal.getRule()).thenReturn(mockLeafRule);

when(mockLeafRule.eClass()).thenReturn(XtextPackage.Literals.PARSER_RULE);
when(mockLeafRule.getAlternatives()).thenReturn(null);
when(mockLeafRule.isDefinesHiddenTokens()).thenReturn(false);
}

// Calls made by doSwitch(grammar)
when(mockGrammar.eClass()).thenReturn(XtextPackage.Literals.GRAMMAR);
when(mockGrammar.isDefinesHiddenTokens()).thenReturn(false);
when(mockGrammar.getUsedGrammars()).thenReturn(new BasicEList<>());

// Calls made per rule by XbaseGeneratorFragmentOverride.usesXImportSection.apply()
setExpectationsForApply(mockRootRule, THIS_PACKAGE_NAME, "rootRule");

final Iterator<PackageAndRuleName> packageAndRuleNameIterator = Arrays.asList(packageAndRuleNamesOfLeafRules).iterator();
for (final ParserRule mockLeafRule : mockLeafRules) {
final PackageAndRuleName packageandRuleName = packageAndRuleNameIterator.next();
final String packageName = packageandRuleName.packageName();
final String ruleName = packageandRuleName.ruleName();
setExpectationsForApply(mockLeafRule, packageName, ruleName);
}
}

/**
* Test usesXImportSection() when passed a null XtextResource
*/
@Test
public void testUsesXImportSectionWithNullGrammar() {
assertThrows(NullPointerException.class, () -> detector.usesXImportSection(null));
}

/**
* Test usesXImportSection() when the grammar does not use xtype::XImportSection().
*/
@Test
@BugTest(value = "DSL-244")
public void testUsesXImportSectionWhenNotUsed() {
// ARRANGE
final Grammar mockGrammar = mock(Grammar.class);

// Use a selection of rules which do not include xtype::XImportSection
setExpectationsForUsesXImportSection(mockGrammar, new PackageAndRuleName(null, "leafRule1"), new PackageAndRuleName(THIS_PACKAGE_NAME, "leafRule2"),
new PackageAndRuleName(XTYPE_PACKAGE_NAME, "leafRule3"), new PackageAndRuleName(null, X_IMPORT_SECTION_RULE_NAME), new PackageAndRuleName(THIS_PACKAGE_NAME, X_IMPORT_SECTION_RULE_NAME));

// ACT
final boolean usesXImportSection = detector.usesXImportSection(mockGrammar);

// ASSERT
assertFalse(usesXImportSection, "usesXImportSection() should return false when the grammar does not use XImportSection");
}

/**
* Test usesXImportSection() when the grammar uses xtype::XImportSection().
*/
@Test
@BugTest(value = "DSL-244")
public void testUsesXImportSectionWhenUsed() {
// ARRANGE
final Grammar mockGrammar = mock(Grammar.class);

// Use a selection of rules including xtype::XImportSection
setExpectationsForUsesXImportSection(mockGrammar, new PackageAndRuleName(null, "leafRule1"), new PackageAndRuleName(THIS_PACKAGE_NAME, "leafRule2"),
new PackageAndRuleName(XTYPE_PACKAGE_NAME, "leafRule3"), new PackageAndRuleName(null, X_IMPORT_SECTION_RULE_NAME), new PackageAndRuleName(THIS_PACKAGE_NAME, X_IMPORT_SECTION_RULE_NAME),
new PackageAndRuleName(XTYPE_PACKAGE_NAME, X_IMPORT_SECTION_RULE_NAME));

// ACT
final boolean usesXImportSection = detector.usesXImportSection(mockGrammar);

// ASSERT
assertTrue(usesXImportSection, "usesXImportSection() should return true when the grammar uses XImportSection");
}
// CHECKSTYLE:CONSTANTS-ON

}
Loading