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
6 changes: 4 additions & 2 deletions com.avaloq.tools.ddk.xtext.export.test/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: com.avaloq.tools.ddk.xtext.export.test
Bundle-SymbolicName: com.avaloq.tools.ddk.xtext.export.test;singleton:=true
Bundle-Version: 17.3.1.qualifier
Bundle-Version: 17.3.2.qualifier
Bundle-Vendor: Avaloq Group AG
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Expand All @@ -13,6 +13,8 @@ Require-Bundle: com.avaloq.tools.ddk.xtext.export,
com.avaloq.tools.ddk.xtext,
junit-jupiter-api,
junit-jupiter-engine,
junit-platform-suite-api
junit-platform-suite-api,
org.eclipse.xtext.xbase,
org.eclipse.xtext.common.types
Export-Package: com.avaloq.tools.ddk.xtext.test.export
Automatic-Module-Name: com.avaloq.tools.ddk.xtext.export.test
2 changes: 1 addition & 1 deletion com.avaloq.tools.ddk.xtext.export.test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<version>18.0.1-SNAPSHOT</version>
<relativePath>../ddk-parent</relativePath>
</parent>
<version>17.3.1-SNAPSHOT</version>
<version>17.3.2-SNAPSHOT</version>

<artifactId>com.avaloq.tools.ddk.xtext.export.test</artifactId>
<packaging>eclipse-test-plugin</packaging>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*******************************************************************************
* Copyright (c) 2026 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.export.jvmmodel;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.impl.ResourceImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.common.types.JvmGenericType;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.resource.XtextResourceSet;
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder;
import org.junit.jupiter.api.Test;

import com.avaloq.tools.ddk.xtext.test.export.util.ExportTestUtil;
import com.avaloq.tools.ddk.xtext.test.jupiter.AbstractXtextTest;


/**
* Tests the JVM model inferred for an export model without an {@code export &lt;name&gt; for &lt;grammar&gt;} header.
* <p>
* That header is optional, so {@code ExportModel.name} is {@code null} for such models. The documentation of the inferred
* resource description manager must then be rendered with an empty name rather than with the string {@code "null"}.
*/
@SuppressWarnings("nls")
public class ExportJvmModelInferrerTest extends AbstractXtextTest {

/** Name of the header-less export model parsed by this test. */
private static final String MODEL_NAME = "HeaderlessExportModel";

/** Source of the header-less export model parsed by this test. */
private static final String MODEL_SOURCE = "import \"http://www.eclipse.org/emf/2002/Ecore\" as ecore\n"
+ "\n"
+ "export ecore::EClass as name\n";

/** Simple name suffix of the inferred resource description manager. */
private static final String MANAGER_SUFFIX = "ResourceDescriptionManager";

/** Documentation expected on the inferred resource description manager; the name is empty, hence the two blanks. */
private static final String EXPECTED_DOCUMENTATION = "Resource description manager for resources.";

@Override
protected ExportTestUtil getXtextTestUtil() {
return ExportTestUtil.getInstance();
}

/**
* This test builds its source in memory and has no test source file. {@inheritDoc}
*/
@Override
protected String getTestSourceFileName() {
return null;
}

@Test
public void testResourceDescriptionManagerDocumentationOfHeaderlessModel() throws IOException {
final Resource resource = parseHeaderlessModel();
final JvmGenericType manager = findInferredManager(resource);
assertNotNull(manager, "The inferrer must infer a resource description manager for a header-less export model.");
final JvmTypesBuilder typesBuilder = getXtextTestUtil().get(JvmTypesBuilder.class);
assertEquals(EXPECTED_DOCUMENTATION, typesBuilder.getDocumentation(manager),
"A null export model name must render as an empty string, not as \"null\".");
}

/**
* Parses the header-less export model and installs its derived state.
* <p>
* The inferrer resolves the grammar belonging to an export model by loading the {@code .xtext} file next to it. An empty
* resource is registered under that URI so the lookup resolves without demand-loading a grammar; the documentation under
* test does not depend on the grammar. Both resources have to share one resource set, hence the explicit set up here
* instead of the usual test utility call, which creates a new resource set per invocation.
*
* @return the loaded resource, never {@code null}
* @throws IOException
* if the model cannot be parsed
*/
private Resource parseHeaderlessModel() throws IOException {
final URI modelUri = getTargetSourceUri(MODEL_NAME + ".export");
final URI grammarUri = modelUri.trimFileExtension().appendFileExtension("xtext");
final Resource grammarResource = new ResourceImpl(grammarUri) {
@Override
public boolean isLoaded() {
return true;
}
};
final XtextResourceSet resourceSet = getXtextTestUtil().getResourceSet();
resourceSet.getResources().add(grammarResource);
resourceSet.getURIResourceMap().put(grammarUri, grammarResource);

final XtextResource resource = (XtextResource) resourceSet.createResource(modelUri);
resourceSet.getResources().add(resource);
resource.load(new ByteArrayInputStream(MODEL_SOURCE.getBytes(StandardCharsets.UTF_8)), null);
EcoreUtil.resolveAll(resource);
return resource;
}

/**
* Returns the inferred resource description manager of the given resource.
*
* @param resource
* the resource of the export model, must not be {@code null}
* @return the inferred type, or {@code null} if the inferrer did not produce one
*/
private JvmGenericType findInferredManager(final Resource resource) {
for (final EObject content : resource.getContents()) {
if (content instanceof JvmGenericType && ((JvmGenericType) content).getSimpleName().endsWith(MANAGER_SUFFIX)) {
return (JvmGenericType) content;
}
}
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.junit.platform.suite.api.Suite;

import com.avaloq.tools.ddk.xtext.export.formatting.ExportFormattingTest;
import com.avaloq.tools.ddk.xtext.export.jvmmodel.ExportJvmModelInferrerTest;
import com.avaloq.tools.ddk.xtext.export.scoping.ExportScopingTest;
import com.avaloq.tools.ddk.xtext.export.validation.ExportValidationOkTest;
import com.avaloq.tools.ddk.xtext.export.validation.ExportValidationTest;
Expand All @@ -23,6 +24,6 @@
* Empty class serving only as holder for JUnit 5 suite annotations.
*/
@Suite
@SelectClasses({ExportFormattingTest.class, ExportValidationTest.class, ExportValidationOkTest.class, ExportScopingTest.class})
@SelectClasses({ExportFormattingTest.class, ExportValidationTest.class, ExportValidationOkTest.class, ExportScopingTest.class, ExportJvmModelInferrerTest.class})
public class ExportTestSuite {
}
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ private void inferResourceDescriptionManager(final ExportModel model, final IJvm
it.getSuperTypes().add(_typeReferenceBuilder.typeRef(AbstractCachingResourceDescriptionManager.class));
it.getAnnotations().add(typeOnlyAnnotation(Singleton.class));
addSuppressWarningsAll(it);
jvmTypesBuilder.setDocumentation(it, "Resource description manager for %s resources.".formatted(model.getName()));
jvmTypesBuilder.setDocumentation(it, "Resource description manager for %s resources.".formatted(Strings.emptyIfNull(model.getName())));
final Procedure1<JvmField> fieldInitializer = (final JvmField field) -> {
field.setVisibility(JvmVisibility.PUBLIC);
field.setStatic(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ private boolean _isThis(final Identifier it) {
}

private String calledFeature(final FeatureCall it) {
return it.getType().getId().get(0);
return it.getType().getId().isEmpty() ? null : it.getType().getId().get(0);
}

private String serialize(final EObject it) {
Expand Down