Skip to content
Draft
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: 1 addition & 0 deletions com.avaloq.tools.ddk.check.core.test/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Bundle-Vendor: Avaloq Group AG
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: com.avaloq.tools.ddk.check.core,
com.avaloq.tools.ddk.xtext,
com.avaloq.tools.ddk.xtext.test.core,
com.avaloq.tools.ddk.check.ui,
org.eclipse.xtext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

package com.avaloq.tools.ddk.check.core.test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
Expand Down Expand Up @@ -96,6 +97,10 @@ public List<JavaSource> generateAndCompile(final InputStream sourceStream) {
fsa.getOutputConfigurations().put(output.getName(), output);
}
generator.doGenerate(res, fsa);
// Generated content must be line-ending-deterministic (LF) on every platform.
for (java.util.Map.Entry<String, CharSequence> file : fsa.getTextFiles().entrySet()) {
assertEquals(-1, file.getValue().toString().indexOf('\r'), "generated file must not contain CR: " + file.getKey());
}
// We now should have a number of files.
String baseName = root.getPackageName() + '.' + root.getName();
String basePath = baseName.replace('.', '/');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*******************************************************************************
* 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.check.core.test;

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

import org.eclipse.xtext.formatting.ILineSeparatorInformation;
import org.eclipse.xtext.testing.InjectWith;
import org.eclipse.xtext.testing.extensions.InjectionExtension;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import com.avaloq.tools.ddk.check.CheckInjectorProvider;
import com.avaloq.tools.ddk.xtext.generator.LfLineSeparatorInformation;
import com.google.inject.Inject;


/**
* Guarantees the Check runtime injector resolves {@link ILineSeparatorInformation} to the LF
* binding, so headless generation is line-ending-deterministic. The other DDK language
* runtime modules declare the identical binding method; this test pins the Guice
* module-convention wiring they all rely on.
*/
@InjectWith(CheckInjectorProvider.class)
@ExtendWith(InjectionExtension.class)
@SuppressWarnings("nls")
public class CheckLineSeparatorBindingTest {

@Inject
private ILineSeparatorInformation lineSeparatorInformation;

@Test
public void runtimeInjectorBindsLfLineSeparator() {
assertInstanceOf(LfLineSeparatorInformation.class, lineSeparatorInformation, "Check runtime injector must bind the LF separator information");
assertEquals("\n", lineSeparatorInformation.getLineSeparator(), "bound separator must be LF");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import com.avaloq.tools.ddk.check.core.generator.IssueCodeValueTest;
import com.avaloq.tools.ddk.check.core.test.BasicModelTest;
import com.avaloq.tools.ddk.check.core.test.CheckLineSeparatorBindingTest;
import com.avaloq.tools.ddk.check.core.test.BugAig1314;
import com.avaloq.tools.ddk.check.core.test.BugAig830;
import com.avaloq.tools.ddk.check.core.test.BugDsl27;
Expand All @@ -35,6 +36,7 @@
// @Format-Off
IssueCodeValueTest.class,
BasicModelTest.class,
CheckLineSeparatorBindingTest.class,
BugAig830.class,
CheckScopingTest.class,
CheckValidationTest.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package com.avaloq.tools.ddk.check;

import org.eclipse.xtext.documentation.IEObjectDocumentationProvider;
import org.eclipse.xtext.formatting.ILineSeparatorInformation;
import org.eclipse.xtext.generator.IOutputConfigurationProvider;
import org.eclipse.xtext.linking.ILinkingService;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
Expand Down Expand Up @@ -40,6 +41,7 @@
import com.avaloq.tools.ddk.check.scoping.ExtensionPointAwareScopeProvider;
import com.avaloq.tools.ddk.check.typing.CheckExpressionHelper;
import com.avaloq.tools.ddk.check.typing.CheckTypeComputer;
import com.avaloq.tools.ddk.xtext.generator.LfLineSeparatorInformation;
import com.google.inject.name.Names;


Expand All @@ -48,6 +50,16 @@
*/
@SuppressWarnings({"PMD.CouplingBetweenObjects", "restriction"})
public class CheckRuntimeModule extends com.avaloq.tools.ddk.check.AbstractCheckRuntimeModule {

/**
* Binds the generated-file line separator to LF so code generation is deterministic
* across platforms; headless builds otherwise fall back to the platform separator.
*
* @return the LF {@link ILineSeparatorInformation} implementation, never {@code null}
*/
public Class<? extends ILineSeparatorInformation> bindILineSeparatorInformation() {
return LfLineSeparatorInformation.class;
}
@Override
public Class<? extends XtextResource> bindXtextResource() {
return CheckBatchLinkableResource.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package com.avaloq.tools.ddk.checkcfg;

import org.eclipse.xtext.conversion.IValueConverterService;
import org.eclipse.xtext.formatting.ILineSeparatorInformation;
import org.eclipse.xtext.generator.IGenerator;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
import org.eclipse.xtext.resource.ILocationInFileProvider;
Expand All @@ -26,6 +27,7 @@
import com.avaloq.tools.ddk.checkcfg.resource.CheckCfgLocationInFileProvider;
import com.avaloq.tools.ddk.checkcfg.scoping.CheckCfgBatchLinkingService;
import com.avaloq.tools.ddk.checkcfg.scoping.CheckCfgScopeProvider;
import com.avaloq.tools.ddk.xtext.generator.LfLineSeparatorInformation;
import com.google.inject.name.Names;


Expand All @@ -34,6 +36,16 @@
*/
public class CheckCfgRuntimeModule extends com.avaloq.tools.ddk.checkcfg.AbstractCheckCfgRuntimeModule {

/**
* Binds the generated-file line separator to LF so code generation is deterministic
* across platforms; headless builds otherwise fall back to the platform separator.
*
* @return the LF {@link ILineSeparatorInformation} implementation, never {@code null}
*/
public Class<? extends ILineSeparatorInformation> bindILineSeparatorInformation() {
return LfLineSeparatorInformation.class;
}

/**
* Custom location in file provider used for revealing and highlighting a model element in the editor.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Workflow {
component = com.avaloq.tools.ddk.xtext.generator.util.CustomClassAwareEcoreGenerator {
genModel = "platform:/resource/${projectName}/model/ModelInference.genmodel"
generateEdit = false
lineDelimiter = "\n"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ Workflow {
component = EcoreGenerator auto-inject {
genModel = "platform:/resource/${projectName}/model/TypeModel.genmodel"
generateEdit=true
lineDelimiter = "\n"
}

component = EcoreGenerator auto-inject {
genModel = "platform:/resource/${projectName}/model/BuiltInTypeModel.genmodel"
generateEdit=false
lineDelimiter = "\n"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,31 @@
*******************************************************************************/
package com.avaloq.tools.ddk.xtext.export;

import org.eclipse.xtext.formatting.ILineSeparatorInformation;
import org.eclipse.xtext.generator.IOutputConfigurationProvider;
import org.eclipse.xtext.naming.IQualifiedNameConverter;

import com.avaloq.tools.ddk.xtext.export.conversion.ExportValueConverterService;
import com.avaloq.tools.ddk.xtext.export.generator.ExportOutputConfigurationProvider;
import com.avaloq.tools.ddk.xtext.export.naming.ExportQualifiedNameConverter;
import com.avaloq.tools.ddk.xtext.generator.LfLineSeparatorInformation;


/**
* Use this class to register components to be used at runtime / without the Equinox extension registry.
*/
public class ExportRuntimeModule extends com.avaloq.tools.ddk.xtext.export.AbstractExportRuntimeModule {

/**
* Binds the generated-file line separator to LF so code generation is deterministic
* across platforms; headless builds otherwise fall back to the platform separator.
*
* @return the LF {@link ILineSeparatorInformation} implementation, never {@code null}
*/
public Class<? extends ILineSeparatorInformation> bindILineSeparatorInformation() {
return LfLineSeparatorInformation.class;
}

/**
* {@inheritDoc}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,27 @@
package com.avaloq.tools.ddk.xtext.expression;

import org.eclipse.xtext.conversion.IValueConverterService;
import org.eclipse.xtext.formatting.ILineSeparatorInformation;

import com.avaloq.tools.ddk.xtext.expression.conversion.ExpressionValueConverterService;
import com.avaloq.tools.ddk.xtext.generator.LfLineSeparatorInformation;


/**
* Use this class to register components to be used at runtime / without the Equinox extension registry.
*/
public class ExpressionRuntimeModule extends AbstractExpressionRuntimeModule {

/**
* Binds the generated-file line separator to LF so code generation is deterministic
* across platforms; headless builds otherwise fall back to the platform separator.
*
* @return the LF {@link ILineSeparatorInformation} implementation, never {@code null}
*/
public Class<? extends ILineSeparatorInformation> bindILineSeparatorInformation() {
return LfLineSeparatorInformation.class;
}

@Override
public Class<? extends IValueConverterService> bindIValueConverterService() {
return ExpressionValueConverterService.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package com.avaloq.tools.ddk.xtext.format;

import org.eclipse.xtext.conversion.IValueConverterService;
import org.eclipse.xtext.formatting.ILineSeparatorInformation;
import org.eclipse.xtext.generator.IOutputConfigurationProvider;
import org.eclipse.xtext.linking.ILinkingService;
import org.eclipse.xtext.linking.LinkingScopeProviderBinding;
Expand All @@ -34,6 +35,7 @@
import com.avaloq.tools.ddk.xtext.format.resource.FormatResourceDescriptionStrategy;
import com.avaloq.tools.ddk.xtext.format.scoping.FormatLinkingService;
import com.avaloq.tools.ddk.xtext.format.scoping.FormatScopeProvider;
import com.avaloq.tools.ddk.xtext.generator.LfLineSeparatorInformation;
import com.google.inject.Binder;
import com.google.inject.name.Names;

Expand All @@ -42,6 +44,16 @@
*/
public class FormatRuntimeModule extends AbstractFormatRuntimeModule {

/**
* Binds the generated-file line separator to LF so code generation is deterministic
* across platforms; headless builds otherwise fall back to the platform separator.
*
* @return the LF {@link ILineSeparatorInformation} implementation, never {@code null}
*/
public Class<? extends ILineSeparatorInformation> bindILineSeparatorInformation() {
return LfLineSeparatorInformation.class;
}

@Override
public Class<? extends XtextResource> bindXtextResource() {
return FormatResource.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Fragment-Host: com.avaloq.tools.ddk.xtext.generator
Require-Bundle: com.avaloq.tools.ddk.test.core,
com.avaloq.tools.ddk.xtext,
com.avaloq.tools.ddk.xtext.expression,
com.avaloq.tools.ddk.xtext.test.core,
org.eclipse.xtend,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
@SelectClasses({
// @Format-Off
CodeGenerationXTest.class,
LfPrintWriterTest.class,
LineEndingDeterminismTest.class,
CompilationContextTest.class,
ExpressionsExtentionsTest.class,
EClassComparatorTest.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*******************************************************************************
* 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.generator.test.generator;

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

import java.io.File;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import com.avaloq.tools.ddk.xtext.generator.parser.antlr.KeywordAnalysisHelper;


/**
* Guarantees that {@code KeywordAnalysisHelper}'s report writer terminates lines with LF on
* every platform. The keyword reports are committed to git, so a platform-dependent
* {@link PrintWriter#println()} would rewrite them on every Windows build. All
* {@code println(...)} overloads are specified to terminate via {@code println()}, so
* asserting the no-argument terminator covers every call site.
*/
@SuppressWarnings("nls")
public class LfPrintWriterTest {

@TempDir
private File tempDir;

@Test
public void lfPrintWriterTerminatesWithLfOnly() throws Exception {
File file = new File(tempDir, "report.txt");
Class<?> lfPrintWriter = Class.forName(KeywordAnalysisHelper.class.getName() + "$LfPrintWriter", true, KeywordAnalysisHelper.class.getClassLoader());
Constructor<?> constructor = lfPrintWriter.getDeclaredConstructor(File.class);
constructor.setAccessible(true);
try (PrintWriter writer = (PrintWriter) constructor.newInstance(file)) {
writer.println("first");
writer.println();
writer.print("second");
writer.println(42);
}
assertEquals("first\n\nsecond42\n", Files.readString(file.toPath(), StandardCharsets.UTF_8), "every println termination must be a bare LF");
}

}
Loading