blob: 797ddf8cfab986e93c91a595c371e40c7ae0ac39 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.sling.feature.modelconverter;
import org.apache.sling.feature.Bundles;
import org.apache.sling.feature.Configurations;
import org.apache.sling.feature.Extension;
import org.apache.sling.feature.ExtensionType;
import org.apache.sling.feature.Extensions;
import org.apache.sling.feature.builder.FeatureProvider;
import org.apache.sling.feature.io.artifacts.ArtifactHandler;
import org.apache.sling.feature.io.artifacts.ArtifactManager;
import org.apache.sling.feature.io.artifacts.ArtifactManagerConfig;
import org.apache.sling.feature.io.json.FeatureJSONReader;
import org.apache.sling.provisioning.model.Artifact;
import org.apache.sling.provisioning.model.ArtifactGroup;
import org.apache.sling.provisioning.model.Configuration;
import org.apache.sling.provisioning.model.Feature;
import org.apache.sling.provisioning.model.KeyValueMap;
import org.apache.sling.provisioning.model.MergeUtility;
import org.apache.sling.provisioning.model.Model;
import org.apache.sling.provisioning.model.ModelConstants;
import org.apache.sling.provisioning.model.ModelUtility;
import org.apache.sling.provisioning.model.ModelUtility.ResolverOptions;
import org.apache.sling.provisioning.model.ModelUtility.VariableResolver;
import org.apache.sling.provisioning.model.RunMode;
import org.apache.sling.provisioning.model.Section;
import org.apache.sling.provisioning.model.io.ModelReader;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class ModelConverterTest {
private Path tempDir;
private ArtifactManager artifactManager;
private FeatureProvider featureProvider;
@Before
public void setup() throws Exception {
String tmpDir = System.getProperty("test.prov.files.tempdir");
if (tmpDir != null) {
tempDir = Paths.get(tmpDir);
System.out.println("*** Using directory for generated files: " + tempDir);
} else {
tempDir = Files.createTempDirectory(getClass().getSimpleName());
}
artifactManager = ArtifactManager.getArtifactManager(
new ArtifactManagerConfig());
featureProvider =
id -> {
try {
URL file = artifactManager.getArtifactHandler(id.toMvnUrl()).getLocalURL();
try (Reader reader = new InputStreamReader(file.openStream(), "UTF-8")) {
return FeatureJSONReader.read(reader, file.toString());
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
};
}
@After
public void tearDown() throws Exception {
if(System.getProperty("test.prov.files.tempdir") == null) {
// Delete the temp dir again
Files.walk(tempDir)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
}
@Test
public void testBootToProvModel() throws Exception {
testConvertToProvisioningModel("/boot.json", "/boot.txt");
}
@Test
public void testBootToFeature() throws Exception {
testConvertToFeature("/boot.txt", "/boot.json");
}
@Test
public void testOakToProvModel() throws Exception {
testConvertToProvisioningModel("/oak.json", "/oak.txt");
}
@Test
public void testSeparateRunmodeFiles() throws Exception {
testConvertToProvisioningModel(
new String[] {
"/runmodeseparation/oak_no_runmode.json",
"/runmodeseparation/oak_mongo.json",
"/runmodeseparation/oak_tar.json"},
"/oak.txt");
}
@Test
public void testDifferentSourceIdenticalProvModel() throws Exception {
String dir1 = System.getProperty("test.json.dir1");
String dir2 = System.getProperty("test.json.dir2");
File[] files1, files2;
if (dir1 != null && dir2 != null) {
files1 = new File(dir1).listFiles((d, n) -> n.endsWith(".json"));
files2 = new File(dir2).listFiles((d, n) -> n.endsWith(".json"));
} else {
files1 = new File[] {
getFile("/runmodeseparation/oak_no_runmode.json"),
getFile("/runmodeseparation/oak_mongo.json"),
getFile("/runmodeseparation/oak_tar.json")
};
files2 = new File[] {getFile("/oak.json")};
}
testConvertToProvisioningModel(files2, files1);
}
private File getFile(String f) throws URISyntaxException {
return new File(getClass().getResource(f).toURI());
}
@Test
public void testOakToFeature() throws Exception {
testConvertToFeature("/oak.txt", "/oak.json");
}
@Test
public void testRepoinitToProvModel() throws Exception {
testConvertToProvisioningModel("/repoinit.json", "/repoinit.txt");
}
@Test
public void testRepoinitToFeature() throws Exception {
testConvertToFeature("/repoinit.txt", "/repoinit.json");
}
@Test
public void testLaunchpadToProvModel() throws Exception {
testConvertToProvisioningModel("/launchpad.json", "/launchpad.txt");
}
@Test
public void testLaunchpadToFeature() throws Exception {
testConvertToFeature("/launchpad.txt", "/launchpad.json");
}
@Test
public void testSimpleToProvModel() throws Exception {
testConvertToProvisioningModel("/simple.json", "/simple.txt");
}
@Test
public void testSimpleToFeature() throws Exception {
testConvertToFeature("/simple.txt", "/simple.json");
}
@Test
public void testSimpleInheritsToProvModel() throws Exception {
testConvertToProvisioningModel("/simple_inherits.json", "/simple_inherits.txt", "/simple.json");
testConvertToProvisioningModel("/simple_inherits.json", "/simple_inherits.txt", "/simple.json", "/simple_inherits.json");
}
@Test
public void testSimpleInheritsViaMavenRepoToProvModel() throws Exception {
artifactManager = Mockito.mock(ArtifactManager.class);
Mockito.when(artifactManager.getArtifactHandler(Mockito.anyString())).then(new Answer<ArtifactHandler>() {
@Override
public ArtifactHandler answer(InvocationOnMock in) throws Throwable {
String url = in.getArgument(0).toString();
if (url.endsWith("simple_inherits.json")) {
return new ArtifactHandler(url, new URL(url));
} else if ("mvn:generated/simple/1.0.0".equals(url)) {
return new ArtifactHandler(url, getClass().getResource("/simple.json"));
}
return null;
}
});
testConvertToProvisioningModel("/simple_inherits.json", "/simple_inherits.txt");
}
@Test
public void testSimple2ToProvModel() throws Exception {
testConvertToProvisioningModel("/simple2.json", "/simple2.txt");
}
@Test
public void testSimple2ToFeature() throws Exception {
testConvertToFeature("/simple2.txt", "/simple2.json");
}
@Test
public void testProvModelRoundtripFolder() throws Exception {
String dir = System.getProperty("test.prov.files.dir");
File filesDir;
if (dir != null) {
filesDir = new File(dir);
} else {
filesDir = new File(getClass().getResource("/repoinit.txt").toURI()).
getParentFile();
}
for (File f : filesDir.listFiles((d, n) -> n.endsWith(".txt"))) {
testConvertFromProvModelRoundTrip(f);
}
}
@Test
public void testModelGAV() throws Exception {
String originalProvModel = "/boot.txt";
String expectedJSON = "/boot_gav.json";
File inFile = new File(getClass().getResource(originalProvModel).toURI());
Map<String, Object> options = new HashMap<>();
options.put("groupId", "testing123");
options.put("version", "4.5.6");
List<File> files = ProvisioningToFeature.convert(inFile, tempDir.toFile(), options);
assertEquals("The testing code expects a single output file here", 1, files.size());
File outFile = files.get(0);
File expectedFile = new File(getClass().getResource(expectedJSON).toURI());
org.apache.sling.feature.Feature expected = FeatureToProvisioning.getFeature(expectedFile);
org.apache.sling.feature.Feature actual = FeatureToProvisioning.getFeature(outFile);
assertFeaturesEqual(expected, actual);
}
@Test
public void testConvertToProvisioningModelOverwriteLogic() throws Exception {
String originalJSON = "/boot.json";
String expectedProvModel = "/boot.txt";
File inFile = new File(getClass().getResource(originalJSON).toURI());
File outFile = new File(tempDir.toFile(), expectedProvModel + ".generated");
FeatureToProvisioning.convert(inFile, outFile, featureProvider);
List<String> orgLines = Files.readAllLines(outFile.toPath());
assertNotEquals("Test precondition", "modified!", orgLines.get(orgLines.size() - 1));
// Append to the output file:
Files.write(outFile.toPath(), "\nmodified!".getBytes(), StandardOpenOption.APPEND);
// Convert again and see that the output file is not modified
FeatureToProvisioning.convert(inFile, outFile, featureProvider);
List<String> lines = Files.readAllLines(outFile.toPath());
assertEquals("modified!", lines.get(lines.size() - 1));
// Modify the modification time of the generated file to be older than the input file
outFile.setLastModified(inFile.lastModified() - 100000);
FeatureToProvisioning.convert(inFile, outFile, featureProvider);
List<String> owLines = Files.readAllLines(outFile.toPath());
assertEquals("The file should have been overwritten since the source has modified since it's edit timestamp",
orgLines, owLines);
}
@Test
public void testConvertToFeature() throws Exception {
File inFile = new File(getClass().getResource("/boot.txt").toURI());
List<File> files = ProvisioningToFeature.convert(inFile, tempDir.toFile(), Collections.emptyMap());
assertEquals("The testing code expects a single output file here", 1, files.size());
File outFile = files.get(0);
List<String> orgLines = Files.readAllLines(outFile.toPath());
assertNotEquals("Test precondition", "modified!", orgLines.get(orgLines.size() - 1));
// Append to the output file:
Files.write(outFile.toPath(), "\nmodified!".getBytes(), StandardOpenOption.APPEND);
// Convert again and see that the output file is not modified
List<File> files2 = ProvisioningToFeature.convert(inFile, tempDir.toFile(), Collections.emptyMap());
assertEquals("Should return the same file list", files, files2);
List<String> lines = Files.readAllLines(outFile.toPath());
assertEquals("modified!", lines.get(lines.size() - 1));
// Modify the modification time of the generated file to be older than the input file
outFile.setLastModified(inFile.lastModified() - 100000);
List<File> files3 = ProvisioningToFeature.convert(inFile, tempDir.toFile(), Collections.emptyMap());
assertEquals("Should return the same file list", files, files3);
List<String> owLines = Files.readAllLines(outFile.toPath());
assertEquals("The file should have been overwritten since the source has modified since it's edit timestamp",
orgLines, owLines);
}
@Test
public void testMultipleRepoinitSections() throws Exception {
testConvertToFeature("/more/repoinit-multiple.txt", "/more/repoinit-multiple.json");
}
public void testConvertFromProvModelRoundTrip(File orgProvModel) throws Exception {
System.out.println("*** Roundtrip converting: " + orgProvModel.getName());
List<File> allGenerateProvisioningModelFiles = new ArrayList<>();
List<File> generated = ProvisioningToFeature.convert(orgProvModel, tempDir.toFile(), Collections.emptyMap());
for (File f : generated) {
String baseName = f.getName().substring(0, f.getName().length() - ".json".length());
assertFalse("File name cannot contain a colon", baseName.contains(":"));
File genFile = new File(tempDir.toFile(), baseName + ".txt");
allGenerateProvisioningModelFiles.add(genFile);
FeatureToProvisioning.convert(f, genFile, featureProvider);
}
Model expected = readProvisioningModel(orgProvModel);
Model actual = readProvisioningModel(allGenerateProvisioningModelFiles);
assertModelsEqual(expected, actual);
}
public void testConvertToFeature(String originalProvModel, String expectedJSON) throws Exception {
File inFile = new File(getClass().getResource(originalProvModel).toURI());
List<File> files = ProvisioningToFeature.convert(inFile, tempDir.toFile(), Collections.emptyMap());
assertEquals("The testing code expects a single output file here", 1, files.size());
File outFile = files.get(0);
File expectedFile = new File(getClass().getResource(expectedJSON).toURI());
org.apache.sling.feature.Feature expected = FeatureToProvisioning.getFeature(expectedFile);
org.apache.sling.feature.Feature actual = FeatureToProvisioning.getFeature(outFile);
assertFeaturesEqual(expected, actual);
}
public void testConvertToProvisioningModel(String originalJSON, String expectedProvModel, String ... additionalContextFiles) throws URISyntaxException, IOException {
File inFile = new File(getClass().getResource(originalJSON).toURI());
File outFile = new File(tempDir.toFile(), expectedProvModel + ".generated");
List<File> addFiles = new ArrayList<>();
for (String af : additionalContextFiles) {
addFiles.add(new File(getClass().getResource(af).toURI()));
}
FeatureToProvisioning.convert(inFile, outFile, featureProvider, addFiles.toArray(new File[] {}));
File expectedFile = new File(getClass().getResource(expectedProvModel).toURI());
Model expected = readProvisioningModel(expectedFile);
Model actual = readProvisioningModel(outFile);
assertModelsEqual(expected, actual);
}
public void testConvertToProvisioningModel(String [] jsonFiles, String expectedProvModel) throws URISyntaxException, IOException {
List<File> inFiles = new ArrayList<>();
for (String jf : jsonFiles) {
inFiles.add(new File(getClass().getResource(jf).toURI()));
}
List<File> generatedFiles = convertFeatureFilesToProvisioningModel(inFiles.toArray(new File[] {}));
File expectedFile = new File(getClass().getResource(expectedProvModel).toURI());
Model expected = readProvisioningModel(expectedFile);
Model actual = readProvisioningModel(generatedFiles);
assertModelsEqual(expected, actual);
assertModelsEqual(actual, expected);
}
public void testConvertToProvisioningModel(File[] jsonFiles1, File[] jsonFiles2) throws URISyntaxException, IOException {
List<File> generatedFiles1 = convertFeatureFilesToProvisioningModel(jsonFiles1);
List<File> generatedFiles2 = convertFeatureFilesToProvisioningModel(jsonFiles2);
Model actual1 = readProvisioningModel(generatedFiles1);
Model actual2 = readProvisioningModel(generatedFiles2);
assertModelsEqual(actual1, actual2);
assertModelsEqual(actual2, actual1);
}
private List<File> convertFeatureFilesToProvisioningModel(File[] jsonFiles) throws URISyntaxException, IOException {
List<File> generatedFiles = new ArrayList<>();
for (File inFile : jsonFiles) {
File outFile;
int counter = 0;
do {
outFile = new File(tempDir.toFile(), inFile.getName() + (counter++) + ".txt.generated");
} while (outFile.exists());
FeatureToProvisioning.convert(inFile, outFile, featureProvider);
generatedFiles.add(outFile);
}
return generatedFiles;
}
private static Model readProvisioningModel(File modelFile) throws IOException {
return readProvisioningModel(Collections.singletonList(modelFile));
}
private static Model readProvisioningModel(List<File> modelFiles) throws IOException {
Model model = null;
for (File modelFile : modelFiles) {
try (FileReader fr = new FileReader(modelFile)) {
Model nextModel = ModelReader.read(fr, modelFile.getAbsolutePath());
if (model == null) {
model = nextModel;
} else {
MergeUtility.merge(model, nextModel);
}
}
}
// Fix the configurations up from the internal format to the Dictionary-based format
return ModelUtility.getEffectiveModel(model,
new ResolverOptions().variableResolver(new VariableResolver() {
@Override
public String resolve(final Feature feature, final String name) {
// Keep variables as-is in the model
return "${" + name + "}";
}
}));
}
private void assertFeaturesEqual(org.apache.sling.feature.Feature expected, org.apache.sling.feature.Feature actual) {
assertEquals(expected.getId(), actual.getId());
assertEquals(expected.getTitle(), actual.getTitle());
assertEquals(expected.getDescription(), actual.getDescription());
assertEquals(expected.getVendor(), actual.getVendor());
assertEquals(expected.getLicense(), actual.getLicense());
assertEquals(expected.getVariables(), actual.getVariables());
assertBundlesEqual(expected.getBundles(), actual.getBundles());
assertConfigurationsEqual(expected.getConfigurations(), actual.getConfigurations(), expected.getBundles(), actual.getBundles());
assertEquals(expected.getFrameworkProperties(), actual.getFrameworkProperties());
assertExtensionEqual(expected.getExtensions(), actual.getExtensions());
// Ignore caps and reqs, prototype and here since they cannot come from the prov model.
}
private void assertBundlesEqual(Bundles expected, Bundles actual) {
for (Iterator<org.apache.sling.feature.Artifact> it = expected.iterator(); it.hasNext(); ) {
org.apache.sling.feature.Artifact ex = it.next();
boolean found = false;
for (Iterator<org.apache.sling.feature.Artifact> it2 = actual.iterator(); it2.hasNext(); ) {
org.apache.sling.feature.Artifact ac = it2.next();
if (ac.getId().equals(ex.getId())) {
found = true;
assertEquals(ex.getMetadata(), ac.getMetadata());
break;
}
}
assertTrue("Not found: " + ex, found);
}
}
private void assertConfigurationsEqual(Configurations expected, Configurations actual, Bundles exBundles, Bundles acBundles) {
for (Iterator<org.apache.sling.feature.Configuration> it = expected.iterator(); it.hasNext(); ) {
org.apache.sling.feature.Configuration ex = it.next();
boolean found = false;
for (Iterator<org.apache.sling.feature.Configuration> it2 = actual.iterator(); it2.hasNext(); ) {
org.apache.sling.feature.Configuration ac = it2.next();
if (ex.getPid().equals(ac.getPid())) {
found = true;
assertConfigProps(ex, ac, exBundles, acBundles);
}
}
assertTrue(found);
}
}
private void assertConfigProps(org.apache.sling.feature.Configuration expected, org.apache.sling.feature.Configuration actual, Bundles exBundles, Bundles acBundles) {
assertTrue("Configurations not equal: " + expected.getProperties() + " vs " + actual.getProperties(),
configPropsEqual(expected.getProperties(), actual.getProperties()));
}
private boolean configPropsEqual(Dictionary<String, Object> d1, Dictionary<String, Object> d2) {
if (d1.size() != d2.size()) {
return false;
}
for (Enumeration<String> e = d1.keys(); e.hasMoreElements(); ) {
String k = e.nextElement();
Object v = d1.get(k);
if (v instanceof Object[]) {
if (!Arrays.equals((Object[]) v, (Object[]) d2.get(k)))
return false;
} else {
if (!v.equals(d2.get(k)))
return false;
}
}
return true;
}
private void assertModelsEqual(Model expected, Model actual) {
for (int i=0; i<expected.getFeatures().size(); i++) {
Feature expectedFeature = expected.getFeatures().get(i);
Feature actualFeature = actual.getFeatures().get(i);
assertFeaturesEqual(expectedFeature, actualFeature);
}
}
private void assertFeaturesEqual(Feature expected, Feature actual) {
assertEquals(expected.getName(), actual.getName());
assertEquals(expected.getVersion(), actual.getVersion());
assertEquals(expected.getType(), actual.getType());
assertRunModesEqual(expected.getName(), expected.getRunModes(), actual.getRunModes());
assertKVMapEquals(expected.getVariables(), actual.getVariables());
assertSectionsEqual(expected.getAdditionalSections(), actual.getAdditionalSections());
}
private void assertRunModesEqual(String featureName, List<RunMode> expected, List<RunMode> actual) {
assertEquals(expected.size(), actual.size());
for (RunMode rm : expected) {
boolean found = false;
for (RunMode arm : actual) {
if (runModesEqual(featureName, rm, arm)) {
found = true;
break;
}
}
if (!found) {
fail("Run Mode " + rm + " not found in actual list " + actual);
}
}
}
private boolean runModesEqual(String featureName, RunMode rm1, RunMode rm2) {
if (rm1.getNames() == null) {
if (rm2.getNames() != null)
return false;
} else {
if (rm2.getNames() == null)
return false;
HashSet<String> names1 = new HashSet<>(Arrays.asList(rm1.getNames()));
HashSet<String> names2 = new HashSet<>(Arrays.asList(rm2.getNames()));
if (!names1.equals(names2))
return false;
}
List<ArtifactGroup> ag1 = rm1.getArtifactGroups();
List<ArtifactGroup> ag2 = rm2.getArtifactGroups();
if (ag1.size() != ag2.size())
return false;
for (ArtifactGroup g1 : ag1) {
boolean found = false;
for (ArtifactGroup g2 : ag2) {
if (artifactGroupsEquals(featureName, g1, g2)) {
found = true;
break;
}
}
if (!found)
return false;
}
List<Configuration> configs1 = new ArrayList<>();
rm1.getConfigurations().iterator().forEachRemaining(configs1::add);
List<Configuration> configs2 = new ArrayList<>();
rm2.getConfigurations().iterator().forEachRemaining(configs2::add);
if (configs1.size() != configs2.size())
return false;
for (int i=0; i < configs1.size(); i++) {
Configuration cfg1 = configs1.get(i);
boolean found = false;
for (Configuration cfg2 : configs2) {
if (cfg1.getFactoryPid() == null) {
if (cfg2.getFactoryPid() != null) {
continue;
}
} else if (!cfg1.getFactoryPid().equals(cfg2.getFactoryPid())) {
continue;
}
if (!cfg1.getPid().equals(cfg2.getPid())) {
continue;
}
// Factory and ordinary PIDs are equal, so check the content
found = true;
if (!configPropsEqual(cfg1.getProperties(), cfg2.getProperties())) {
return false;
}
break;
}
if (!found) {
// Configuration with this PID not found
return false;
}
}
Map<String, String> m1 = kvToMap(rm1.getSettings());
Map<String, String> m2 = kvToMap(rm2.getSettings());
return m1.equals(m2);
}
private Map<String, String> kvToMap(KeyValueMap<String> kvm) {
Map<String, String> m = new HashMap<>();
for (Map.Entry<String, String> entry : kvm) {
m.put(entry.getKey(), entry.getValue());
}
return m;
}
private boolean artifactGroupsEquals(String featureName, ArtifactGroup g1, ArtifactGroup g2) {
int sl1 = effectiveStartLevel(featureName, g1.getStartLevel());
int sl2 = effectiveStartLevel(featureName, g2.getStartLevel());
if (sl1 != sl2)
return false;
List<Artifact> al1 = new ArrayList<>();
g1.iterator().forEachRemaining(al1::add);
List<Artifact> al2 = new ArrayList<>();
g2.iterator().forEachRemaining(al2::add);
for (int i=0; i<al1.size(); i++) {
Artifact a1 = al1.get(i);
boolean found = false;
for (Iterator<Artifact> it = al2.iterator(); it.hasNext(); ) {
Artifact a2 = it.next();
if (a1.compareTo(a2) == 0) {
found = true;
it.remove();
}
}
if (!found) {
return false;
}
}
// Should have found all artifacts
return (al2.size() == 0);
}
private int effectiveStartLevel(String featureName, int startLevel) {
if (startLevel != 0)
return startLevel;
if (ModelConstants.FEATURE_BOOT.equals(featureName)) {
return 1;
} else {
return 20;
}
}
private void assertKVMapEquals(KeyValueMap<String> expected, KeyValueMap<String> actual) {
assertEquals(kvToMap(expected), kvToMap(actual));
}
private void assertExtensionEqual(Extensions expected, Extensions actual) {
assertEquals(expected.size(), actual.size());
for (int i=0; i<expected.size(); i++) {
// TODO support the fact that they may be out of order
Extension ex = expected.get(i);
Extension ac = actual.get(i);
assertEquals(ex.getType(), ac.getType());
assertEquals(ex.getName(), ac.getName());
assertEquals(ex.isRequired(), ac.isRequired());
if (ex.getType() == ExtensionType.TEXT) {
String exTxt = ex.getText().replaceAll("\\s+", "");
String acTxt = ac.getText().replaceAll("\\s+", "");
assertEquals(exTxt, acTxt);
} else if (ex.getType() == ExtensionType.JSON) {
String exJson = ex.getJSON().replaceAll("\\s+", "").replaceAll("\"\",", "");
String acJson = ac.getJSON().replaceAll("\\s+", "").replaceAll("\"\",", "");
assertEquals(exJson, acJson);
}
/* TODO reinstantiate for Artifacts extentions
assertEquals(ex.getArtifacts().size(), ac.getArtifacts().size());
for (int j = 0; j<ex.getArtifacts().size(); j++) {
org.apache.sling.feature.Artifact exa = ex.getArtifacts().get(j);
org.apache.sling.feature.Artifact aca = ac.getArtifacts().get(j);
assertEquals(exa.getId().toMvnId(), aca.getId().toMvnId());
}
*/
}
}
private void assertSectionsEqual(List<Section> expected, List<Section> actual) {
assertEquals(expected.size(), actual.size());
for (int i=0; i<expected.size(); i++) {
Section esec = expected.get(i);
Section asec = actual.get(i);
assertEquals(esec.getName(), asec.getName());
assertEquals(esec.getContents().replaceAll("\\s+", ""),
asec.getContents().replaceAll("\\s+", ""));
assertEquals(esec.getAttributes(), asec.getAttributes());
}
}
}