blob: 62766ea4ee8ac501a1b606b36293f7b1913241d1 [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.extension.apiregions;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonReader;
import org.apache.sling.feature.ArtifactId;
import org.apache.sling.feature.Extension;
import org.apache.sling.feature.ExtensionState;
import org.apache.sling.feature.ExtensionType;
import org.apache.sling.feature.Feature;
import org.apache.sling.feature.builder.HandlerContext;
import org.apache.sling.feature.extension.apiregions.api.ApiExport;
import org.apache.sling.feature.extension.apiregions.api.ApiRegion;
import org.apache.sling.feature.extension.apiregions.api.ApiRegions;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class APIRegionMergeHandlerTest {
private Path tempDir;
@Before
public void setUp() throws IOException {
tempDir = Files.createTempDirectory(getClass().getSimpleName());
}
@After
public void tearDown() throws IOException {
// Delete the temp dir again
Files.walk(tempDir)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
@Test
public void testCanMerge() {
APIRegionMergeHandler armh = new APIRegionMergeHandler();
Extension ex = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
assertTrue(armh.canMerge(ex));
assertFalse(armh.canMerge(new Extension(ExtensionType.JSON, "foo", ExtensionState.OPTIONAL)));
}
@Test
public void testAPIRegionMerging() throws Exception {
APIRegionMergeHandler armh = new APIRegionMergeHandler();
Feature tf = new Feature(ArtifactId.fromMvnId("x:t:1"));
Feature sf = new Feature(ArtifactId.fromMvnId("y:s:2"));
Extension tgEx = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
tgEx.setJSON("[{\"name\":\"global\","
+ "\"exports\": [\"a.b.c\",\"d.e.f\"]},"
+ "{\"name\":\"internal\","
+ "\"exports\":[\"xyz\"],"
+ "\"some-key\":\"some-val\"}]");
Extension srEx = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
srEx.setJSON("[{\"name\":\"global\","
+ "\"exports\": [\"test\"]},"
+ "{\"name\":\"something\","
+ "\"exports\": [\"a.ha\"],"
+ "\"my-key\": \"my-val\"}]");
HandlerContext hc = Mockito.mock(HandlerContext.class);
armh.merge(hc, tf, sf, tgEx, srEx);
ApiRegions expected = new ApiRegions();
ApiRegion global = new ApiRegion("global");
global.add(new ApiExport("a.b.c"));
global.add(new ApiExport("d.e.f"));
global.add(new ApiExport("test"));
global.setFeatureOrigins(sf.getId());
expected.add(global);
ApiRegion internal = new ApiRegion("internal");
internal.add(new ApiExport("xyz"));
internal.getProperties().put("some-key", "some-val");
expected.add(internal);
ApiRegion something = new ApiRegion("something");
something.add(new ApiExport("a.ha"));
something.getProperties().put("my-key", "my-val");
something.setFeatureOrigins(sf.getId());
expected.add(something);
ApiRegions created = ApiRegions.parse((JsonArray) tgEx.getJSONStructure());
assertEquals(expected, created);
}
@Test
public void testRegionExportsNoInheritance() throws Exception {
APIRegionMergeHandler armh = new APIRegionMergeHandler();
Feature tf = new Feature(ArtifactId.fromMvnId("x:t:1"));
Feature sf = new Feature(ArtifactId.fromMvnId("y:s:2"));
Extension srEx = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
srEx.setJSON("[{\"name\":\"global\","
+ "\"exports\": [\"a.b.c\",\"d.e.f\"]},"
+ "{\"name\":\"deprecated\","
+ "\"exports\":[\"klm\",\"#ignored\",\"qrs\"]},"
+ "{\"name\":\"internal\","
+ "\"exports\":[\"xyz\"]},"
+ "{\"name\":\"forbidden\","
+ "\"exports\":[\"abc\",\"klm\"]}]");
HandlerContext hc = Mockito.mock(HandlerContext.class);
armh.merge(hc, tf, sf, null, srEx);
Extension tgEx = tf.getExtensions().iterator().next();
String expectedJSON = "[{\"name\":\"global\",\"exports\":[\"a.b.c\",\"d.e.f\"],\"feature-origins\":[\"y:s:2\"]},"
+ "{\"name\":\"deprecated\",\"exports\":[\"klm\",\"qrs\"],\"feature-origins\":[\"y:s:2\"]},"
+ "{\"name\":\"internal\",\"exports\":[\"xyz\"],\"feature-origins\":[\"y:s:2\"]},"
+ "{\"name\":\"forbidden\",\"exports\":[\"abc\",\"klm\"],\"feature-origins\":[\"y:s:2\"]}]";
JsonReader er = Json.createReader(new StringReader(expectedJSON));
JsonReader ar = Json.createReader(new StringReader(tgEx.getJSON()));
JsonArray ea = er.readArray();
JsonArray aa = ar.readArray();
assertEquals(ea, aa);
}
@Test
public void testAPIRegioOrdering() throws Exception {
APIRegionMergeHandler armh = new APIRegionMergeHandler();
Feature f1 = new Feature(ArtifactId.fromMvnId("x:t:1"));
Feature f2 = new Feature(ArtifactId.fromMvnId("y:s:2"));
final ApiRegions regionsF1 = new ApiRegions();
final ApiRegion middleF1 = new ApiRegion("middle");
regionsF1.add(middleF1);
final Extension extF1 = new Extension(ExtensionType.JSON, ApiRegions.EXTENSION_NAME, ExtensionState.OPTIONAL);
extF1.setJSONStructure(regionsF1.toJSONArray());
f1.getExtensions().add(extF1);
final ApiRegions regionsF2 = new ApiRegions();
final ApiRegion topF2 = new ApiRegion("top");
final ApiRegion middleF2 = new ApiRegion("middle");
final ApiRegion bottomF2 = new ApiRegion("bottom");
regionsF2.add(topF2);
regionsF2.add(middleF2);
regionsF2.add(bottomF2);
final Extension extF2 = new Extension(ExtensionType.JSON, ApiRegions.EXTENSION_NAME, ExtensionState.OPTIONAL);
extF2.setJSONStructure(regionsF2.toJSONArray());
f2.getExtensions().add(extF2);
HandlerContext hc = Mockito.mock(HandlerContext.class);
armh.merge(hc, f1, f2, extF1, extF2);
// order must be top - middle - bottom
final ApiRegions result = ApiRegions.parse((JsonArray)extF1.getJSONStructure());
assertEquals(3, result.getRegionNames().size());
assertEquals("top", result.getRegionNames().get(0));
assertEquals("middle", result.getRegionNames().get(1));
assertEquals("bottom", result.getRegionNames().get(2));
}
@Test
public void testAPIRegioOrdering2() throws Exception {
testAPIRegionOrdering(
Arrays.asList("middle"),
Arrays.asList("top", "middle", "bottom"),
Arrays.asList("top", "middle", "bottom"));
testAPIRegionOrdering(
Arrays.asList("r2", "r6", "r7"),
Arrays.asList("r1", "r2", "r3", "r4", "r5", "r7", "r8"),
Arrays.asList("r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8"));
testAPIRegionOrdering(
Arrays.asList("r2", "r3", "r6"),
Arrays.asList("r1", "r2", "r3", "r4", "r5", "r6"),
Arrays.asList("r1", "r2", "r3", "r4", "r5", "r6"));
}
private void testAPIRegionOrdering(List<String> targetNames, List<String> sourceNames, List<String> resultNames) throws Exception {
APIRegionMergeHandler armh = new APIRegionMergeHandler();
Feature f1 = new Feature(ArtifactId.fromMvnId("x:t:1"));
Feature f2 = new Feature(ArtifactId.fromMvnId("y:s:2"));
final ApiRegions regionsF1 = new ApiRegions();
for (String s : targetNames) {
ApiRegion r = new ApiRegion(s);
regionsF1.add(r);
}
final Extension extF1 = new Extension(ExtensionType.JSON, ApiRegions.EXTENSION_NAME, ExtensionState.OPTIONAL);
extF1.setJSONStructure(regionsF1.toJSONArray());
f1.getExtensions().add(extF1);
final ApiRegions regionsF2 = new ApiRegions();
for (String s : sourceNames) {
ApiRegion r = new ApiRegion(s);
regionsF2.add(r);
}
final Extension extF2 = new Extension(ExtensionType.JSON, ApiRegions.EXTENSION_NAME, ExtensionState.OPTIONAL);
extF2.setJSONStructure(regionsF2.toJSONArray());
f2.getExtensions().add(extF2);
HandlerContext hc = Mockito.mock(HandlerContext.class);
armh.merge(hc, f1, f2, extF1, extF2);
// order must be top - middle - bottom
final ApiRegions result = ApiRegions.parse((JsonArray)extF1.getJSONStructure());
List<String> actualNames = new ArrayList<>();
for (String s : result.getRegionNames()) {
actualNames.add(s);
}
assertEquals(resultNames, actualNames);
}
}