From bbb56de1b8f5ab2322fea0a7537bfe1b9c23a366 Mon Sep 17 00:00:00 2001 From: "k.kedron" Date: Fri, 14 Jun 2019 09:09:39 +0200 Subject: Improve the unit tests for StringTransformAction, UnaryFieldAction and TopoSearchAction Add new unit test for StringTransformAction, UnaryFieldAction. Improve the TopoSearchActionTest: - use new ActionBuilder to creates the objects - use Mocikto mock instead inner BaseAction class Issue-ID: SDC-2327 Signed-off-by: Krystian Kedron Change-Id: I049eede5bf9102bfc1f7b9527402a20b116bae30 --- .../restmodels/ruleeditor/ActionBuilder.java | 38 +++ .../ruleeditor/StringTransformActionTest.java | 104 ++++++-- .../ruleeditor/TopoSearchActionTest.java | 267 ++++++++++----------- .../ruleeditor/UnaryFieldActionTest.java | 105 ++++++-- 4 files changed, 326 insertions(+), 188 deletions(-) create mode 100644 src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/ActionBuilder.java (limited to 'src') diff --git a/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/ActionBuilder.java b/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/ActionBuilder.java new file mode 100644 index 0000000..cb9e3a0 --- /dev/null +++ b/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/ActionBuilder.java @@ -0,0 +1,38 @@ +/*- + * ============LICENSE_START=============================================== + * ONAP SDC + * ======================================================================== + * Copyright (C) 2019 Samsung. All rights reserved. + * ======================================================================== + * Licensed 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. + * ============LICENSE_END================================================= + */ + +package org.onap.sdc.dcae.composition.restmodels.ruleeditor; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +class ActionBuilder { + + private static Gson gson = + new GsonBuilder().registerTypeAdapter(BaseAction.class, new ActionDeserializer()) + .registerTypeAdapter(BaseCondition.class, new ConditionDeserializer()).create(); + + private ActionBuilder() { + } + + static T buildAction(String action, Class clazz) { + return gson.fromJson(action, clazz); + } +} diff --git a/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/StringTransformActionTest.java b/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/StringTransformActionTest.java index 573e0a4..239ac2f 100644 --- a/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/StringTransformActionTest.java +++ b/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/StringTransformActionTest.java @@ -1,29 +1,87 @@ +/*- + * ============LICENSE_START=============================================== + * ONAP SDC + * ======================================================================== + * Modifications Copyright (c) 2019 Samsung + * ======================================================================== + * Licensed 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. + * ============LICENSE_END================================================= + */ + package org.onap.sdc.dcae.composition.restmodels.ruleeditor; -import org.junit.Test; +import static com.google.code.beanmatchers.BeanMatchers.hasValidBeanConstructor; +import static com.google.code.beanmatchers.BeanMatchers.hasValidBeanEquals; +import static com.google.code.beanmatchers.BeanMatchers.hasValidBeanHashCodeFor; +import static com.google.code.beanmatchers.BeanMatchers.hasValidGettersAndSetters; -import static com.google.code.beanmatchers.BeanMatchers.*; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +@RunWith(MockitoJUnitRunner.class) public class StringTransformActionTest { - @Test - public void shouldHaveValidGettersAndSetters() { - assertThat(StringTransformAction.class, hasValidGettersAndSetters()); - } - - @Test - public void checkEquals() { - assertThat(StringTransformAction.class, hasValidBeanEquals()); - } - - @Test - public void testHasValidConstructor() { - assertThat(StringTransformAction.class, hasValidBeanConstructor()); - } - - @Test - public void checkHashCodeFor() { - assertThat(StringTransformAction.class, hasValidBeanHashCodeFor()); - } -} \ No newline at end of file + private static final String FIRST_START_VALUE = "test1"; + private static final String START_VALUES = "${" + FIRST_START_VALUE + "}${test2}"; + private static final String TEST_TARGET = "testTarget"; + private static final String IS_TRIM = "true"; + + @Mock + private BaseAction baseAction; + + @Test + public void shouldHaveValidGettersAndSetters() { + assertThat(StringTransformAction.class, hasValidGettersAndSetters()); + } + + @Test + public void checkEquals() { + assertThat(StringTransformAction.class, hasValidBeanEquals()); + } + + @Test + public void testHasValidConstructor() { + assertThat(StringTransformAction.class, hasValidBeanConstructor()); + } + + @Test + public void checkHashCodeFor() { + assertThat(StringTransformAction.class, hasValidBeanHashCodeFor()); + } + + @Test + public void stringTransformActionBaseFieldTest() { + StringTransformAction stringTransformAction = buildBasestringTransformAction(); + + assertEquals(START_VALUES, stringTransformAction.startValue()); + assertEquals(TEST_TARGET, stringTransformAction.targetCase()); + assertTrue(stringTransformAction.trim()); + + Mockito.when(baseAction.strippedTarget()).thenReturn(FIRST_START_VALUE); + assertTrue(stringTransformAction.referencesTarget(baseAction)); + } + + private StringTransformAction buildBasestringTransformAction() { + String stringTransform = "{stringTransform: {startValue:\"" + START_VALUES + "\"," + + "targetCase:" + TEST_TARGET + "," + "isTrimString:" + IS_TRIM + "}}"; + + return ActionBuilder.buildAction(stringTransform, StringTransformAction.class); + } +} diff --git a/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/TopoSearchActionTest.java b/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/TopoSearchActionTest.java index d052d26..36bee93 100644 --- a/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/TopoSearchActionTest.java +++ b/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/TopoSearchActionTest.java @@ -31,153 +31,134 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - import java.util.List; import java.util.Map; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +@RunWith(MockitoJUnitRunner.class) public class TopoSearchActionTest { - private static Gson gson = new GsonBuilder() - .registerTypeAdapter(BaseAction.class, new ActionDeserializer()) - .registerTypeAdapter(BaseCondition.class, new ConditionDeserializer()).create(); - - private static final String ACTION_TYPE = "Test Topology Search"; - private static final String ID = "123"; - private static final String SEARCH_FIELD = "sourceToSearch"; - private static final String SEARCH_STRING = "searchString"; - private static final String SEARCH_VALUE = "${"+ SEARCH_STRING +"}"; - private static final String RADIO = "1234"; - private static final String SEARCH_FILTER_LEFT = "${event.commonEventHeader.eventId}"; - private static final String SEARCH_FILTER_RIGHT_ITEM = "testItem"; - private static final String SEARCH_FILTER_RIGHT_ARRAY = "["+ SEARCH_FILTER_RIGHT_ITEM +"]"; - private static final String SEARCH_FILTER_OPERATOR = "TestOperator"; - private static final String ENRICH_FIELD1 = "enrichFiled1"; - private static final String ENRICH_FIELD2 = "enrichField2"; - private static final String ENRICH_FIELDS_ARRAY = "[{value:"+ ENRICH_FIELD1 +"},{value:"+ ENRICH_FIELD2 +"}]"; - private static final String ENRICH_PREFIX = "enrichPrefix"; - - private static final String UI_HASH_MAP_KEY1 = "testKey1"; - private static final String UI_HASH_MAP_KEY2 = "testKey2"; - private static final String UI_HASH_MAP_VALUE1 = "test1"; - private static final String UI_HASH_MAP_VALUE2 = "test2"; - - private static final String BAD_STRIPPED_TARGET = "badTarget"; - - @Test - public void shouldHaveValidGettersAndSetters() { - assertThat(TopoSearchAction.class, hasValidGettersAndSetters()); - } - - @Test - public void checkEquals() { - assertThat(TopoSearchAction.class, hasValidBeanEquals()); - } - - @Test - public void testHasValidConstructor() { - assertThat(TopoSearchAction.class, hasValidBeanConstructor()); - } - - @Test - public void checkHashCodeFor() { - assertThat(TopoSearchAction.class, hasValidBeanHashCodeFor()); - } - - @Test - public void topSearchActionBaseFieldTest() { - TopoSearchAction topoSearchAction = buildBaseConditionalTopoSearchAction(); - - assertEquals(SEARCH_FIELD, topoSearchAction.searchField()); - assertEquals(SEARCH_VALUE, topoSearchAction.searchValue()); - assertNotNull(topoSearchAction.searchFilter()); - - Condition filter = topoSearchAction.searchFilter(); - assertEquals(SEARCH_FILTER_LEFT, filter.left); - assertEquals(SEARCH_FILTER_RIGHT_ITEM, filter.right.get(0)); - assertEquals(SEARCH_FILTER_OPERATOR, filter.operator); - - assertEquals(ENRICH_PREFIX, topoSearchAction.enrichPrefix()); - assertNotNull(topoSearchAction.enrichFields()); - - List enrichFields = topoSearchAction.enrichFields(); - assertEquals(ENRICH_FIELD1, enrichFields.get(0)); - assertEquals(ENRICH_FIELD2, enrichFields.get(1)); - - assertTrue(topoSearchAction.doEnrich()); - } - - @Test - public void topSearchActionUpdatesTest() { - TopoSearchAction topoSearchAction = buildConditionalTopoSearchActionWithUpdates(); - assertNotNull(topoSearchAction.updates()); - - List uiHashMapList = topoSearchAction.updates(); - assertEquals(UI_HASH_MAP_VALUE1, uiHashMapList.get(0).getValue()); - assertEquals(UI_HASH_MAP_VALUE2, uiHashMapList.get(1).getValue()); - assertEquals(UI_HASH_MAP_KEY1, uiHashMapList.get(0).getKey()); - assertEquals(UI_HASH_MAP_KEY2, uiHashMapList.get(1).getKey()); - - Map updatesMap = topoSearchAction.updatesMap(); - assertEquals(UI_HASH_MAP_VALUE1, updatesMap.get(UI_HASH_MAP_KEY1)); - assertEquals(UI_HASH_MAP_VALUE2, updatesMap.get(UI_HASH_MAP_KEY2)); - } - - @Test - public void checkReferencesTargetTest() { - TopoSearchAction topoSearchAction = buildBaseConditionalTopoSearchAction(); - - BaseAction baseAction = new TestBaseAction(SEARCH_STRING); - BaseAction baseActionWithBadTarget = new TestBaseAction(BAD_STRIPPED_TARGET); - - assertTrue(topoSearchAction.referencesTarget(baseAction)); - assertFalse(topoSearchAction.referencesTarget(baseActionWithBadTarget)); - } - - private TopoSearchAction buildBaseConditionalTopoSearchAction() { - String topoSearch = "{actionType:\"" + ACTION_TYPE + "\"," + - "id:" + ID + "," + - "search:{searchField:" + SEARCH_FIELD + "," + - "searchValue:\"" + SEARCH_VALUE + "\"," + - "radio:" + RADIO + "," + - "searchFilter:{left:\"" + SEARCH_FILTER_LEFT + "\"," + - "right:" + SEARCH_FILTER_RIGHT_ARRAY + "," + - "operator:" + SEARCH_FILTER_OPERATOR + "}," + - "enrich:{fields:" + ENRICH_FIELDS_ARRAY + "," + - "prefix:" + ENRICH_PREFIX + "}}}"; - return buildTopoSearchAction(topoSearch); - } - - private TopoSearchAction buildConditionalTopoSearchActionWithUpdates() { - String topoSearch = "{search:{updates: [{key:" + UI_HASH_MAP_KEY1 + ", value:" + UI_HASH_MAP_VALUE1 + "}," + - "{key:" + UI_HASH_MAP_KEY2 + ", value:" + UI_HASH_MAP_VALUE2 + "}]}}"; - - return buildTopoSearchAction(topoSearch); - } - - private TopoSearchAction buildTopoSearchAction(String topoSearch) { - return gson.fromJson(topoSearch, TopoSearchAction.class); - } - - private class TestBaseAction extends BaseAction { - - private String strippedTarget; - - TestBaseAction(String strippedTarget) { - this.strippedTarget = strippedTarget; - } - - @Override - public boolean referencesTarget(BaseAction other) { - return false; - } - - @Override - public String strippedTarget() { - return strippedTarget; - } - } -} \ No newline at end of file + private static final String ACTION_TYPE = "Test Topology Search"; + private static final String ID = "123"; + private static final String SEARCH_FIELD = "sourceToSearch"; + private static final String SEARCH_STRING = "searchString"; + private static final String SEARCH_VALUE = "${" + SEARCH_STRING + "}"; + private static final String RADIO = "1234"; + private static final String SEARCH_FILTER_LEFT = "${event.commonEventHeader.eventId}"; + private static final String SEARCH_FILTER_RIGHT_ITEM = "testItem"; + private static final String SEARCH_FILTER_RIGHT_ARRAY = "[" + SEARCH_FILTER_RIGHT_ITEM + "]"; + private static final String SEARCH_FILTER_OPERATOR = "TestOperator"; + private static final String ENRICH_FIELD1 = "enrichFiled1"; + private static final String ENRICH_FIELD2 = "enrichField2"; + private static final String ENRICH_FIELDS_ARRAY = + "[{value:" + ENRICH_FIELD1 + "},{value:" + ENRICH_FIELD2 + "}]"; + private static final String ENRICH_PREFIX = "enrichPrefix"; + + private static final String UI_HASH_MAP_KEY1 = "testKey1"; + private static final String UI_HASH_MAP_KEY2 = "testKey2"; + private static final String UI_HASH_MAP_VALUE1 = "test1"; + private static final String UI_HASH_MAP_VALUE2 = "test2"; + + private static final String BAD_STRIPPED_TARGET = "badTarget"; + + @Mock + BaseAction baseAction; + @Mock + BaseAction baseActionWithBadTarget; + + @Test + public void shouldHaveValidGettersAndSetters() { + assertThat(TopoSearchAction.class, hasValidGettersAndSetters()); + } + + @Test + public void checkEquals() { + assertThat(TopoSearchAction.class, hasValidBeanEquals()); + } + + @Test + public void testHasValidConstructor() { + assertThat(TopoSearchAction.class, hasValidBeanConstructor()); + } + + @Test + public void checkHashCodeFor() { + assertThat(TopoSearchAction.class, hasValidBeanHashCodeFor()); + } + + @Test + public void topSearchActionBaseFieldTest() { + TopoSearchAction topoSearchAction = buildBaseConditionalTopoSearchAction(); + + assertEquals(SEARCH_FIELD, topoSearchAction.searchField()); + assertEquals(SEARCH_VALUE, topoSearchAction.searchValue()); + assertNotNull(topoSearchAction.searchFilter()); + + Condition filter = topoSearchAction.searchFilter(); + assertEquals(SEARCH_FILTER_LEFT, filter.left); + assertEquals(SEARCH_FILTER_RIGHT_ITEM, filter.right.get(0)); + assertEquals(SEARCH_FILTER_OPERATOR, filter.operator); + + assertEquals(ENRICH_PREFIX, topoSearchAction.enrichPrefix()); + assertNotNull(topoSearchAction.enrichFields()); + + List enrichFields = topoSearchAction.enrichFields(); + assertEquals(ENRICH_FIELD1, enrichFields.get(0)); + assertEquals(ENRICH_FIELD2, enrichFields.get(1)); + + assertTrue(topoSearchAction.doEnrich()); + } + + @Test + public void topSearchActionUpdatesTest() { + TopoSearchAction topoSearchAction = buildConditionalTopoSearchActionWithUpdates(); + assertNotNull(topoSearchAction.updates()); + + List uiHashMapList = topoSearchAction.updates(); + assertEquals(UI_HASH_MAP_VALUE1, uiHashMapList.get(0).getValue()); + assertEquals(UI_HASH_MAP_VALUE2, uiHashMapList.get(1).getValue()); + assertEquals(UI_HASH_MAP_KEY1, uiHashMapList.get(0).getKey()); + assertEquals(UI_HASH_MAP_KEY2, uiHashMapList.get(1).getKey()); + + Map updatesMap = topoSearchAction.updatesMap(); + assertEquals(UI_HASH_MAP_VALUE1, updatesMap.get(UI_HASH_MAP_KEY1)); + assertEquals(UI_HASH_MAP_VALUE2, updatesMap.get(UI_HASH_MAP_KEY2)); + } + + @Test + public void checkReferencesTargetTest() { + TopoSearchAction topoSearchAction = buildBaseConditionalTopoSearchAction(); + + Mockito.when(baseAction.strippedTarget()).thenReturn(SEARCH_STRING); + Mockito.when(baseActionWithBadTarget.strippedTarget()).thenReturn(BAD_STRIPPED_TARGET); + + assertTrue(topoSearchAction.referencesTarget(baseAction)); + assertFalse(topoSearchAction.referencesTarget(baseActionWithBadTarget)); + } + + private TopoSearchAction buildBaseConditionalTopoSearchAction() { + String topoSearch ="{actionType:\"" + ACTION_TYPE + "\"," + + "id:" + ID + "," + + "search:{searchField:" + SEARCH_FIELD + "," + + "searchValue:\"" + SEARCH_VALUE + "\"," + + "radio:" + RADIO + "," + + "searchFilter:{left:\"" + SEARCH_FILTER_LEFT + "\"," + + "right:" + SEARCH_FILTER_RIGHT_ARRAY + "," + + "operator:" + SEARCH_FILTER_OPERATOR + "}," + + "enrich:{fields:" + ENRICH_FIELDS_ARRAY + "," + + "prefix:" + ENRICH_PREFIX + "}}}"; + return ActionBuilder.buildAction(topoSearch, TopoSearchAction.class); + } + + private TopoSearchAction buildConditionalTopoSearchActionWithUpdates() { + String topoSearch = "{search:{updates: [{key:" + UI_HASH_MAP_KEY1 + ", value:" + UI_HASH_MAP_VALUE1 + "}," + + "{key:" + UI_HASH_MAP_KEY2 + ", value:" + UI_HASH_MAP_VALUE2 + "}]}}"; + + return ActionBuilder.buildAction(topoSearch, TopoSearchAction.class); + } +} diff --git a/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/UnaryFieldActionTest.java b/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/UnaryFieldActionTest.java index 2ead066..8fed84d 100644 --- a/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/UnaryFieldActionTest.java +++ b/src/test/java/org/onap/sdc/dcae/composition/restmodels/ruleeditor/UnaryFieldActionTest.java @@ -1,29 +1,90 @@ +/*- + * ============LICENSE_START=============================================== + * ONAP SDC + * ======================================================================== + * Modifications Copyright (c) 2019 Samsung + * ======================================================================== + * Licensed 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. + * ============LICENSE_END================================================= + */ + package org.onap.sdc.dcae.composition.restmodels.ruleeditor; +import static com.google.code.beanmatchers.BeanMatchers.hasValidBeanConstructor; +import static com.google.code.beanmatchers.BeanMatchers.hasValidBeanEquals; +import static com.google.code.beanmatchers.BeanMatchers.hasValidBeanHashCodeFor; +import static com.google.code.beanmatchers.BeanMatchers.hasValidGettersAndSetters; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + import org.junit.Test; -import static com.google.code.beanmatchers.BeanMatchers.*; -import static org.junit.Assert.*; +import java.util.List; public class UnaryFieldActionTest { - @Test - public void shouldHaveValidGettersAndSetters() { - assertThat(UnaryFieldAction.class, hasValidGettersAndSetters()); - } - - @Test - public void checkEquals() { - assertThat(UnaryFieldAction.class, hasValidBeanEquals()); - } - - @Test - public void testHasValidConstructor() { - assertThat(UnaryFieldAction.class, hasValidBeanConstructor()); - } - - @Test - public void checkHashCodeFor() { - assertThat(UnaryFieldAction.class, hasValidBeanHashCodeFor()); - } -} \ No newline at end of file + private static final String REGEX = "testRegex"; + private static final String STATE = "DONE"; + private static final String VALUE_STRING_MAIN = "from"; + private static final String VALUE_STRING_INSIDE = "fromField"; + private static final String VALUE_ARRAY = "[{value:" + VALUE_STRING_INSIDE + "}]"; + + @Test + public void shouldHaveValidGettersAndSetters() { + assertThat(UnaryFieldAction.class, hasValidGettersAndSetters()); + } + + @Test + public void checkEquals() { + assertThat(UnaryFieldAction.class, hasValidBeanEquals()); + } + + @Test + public void testHasValidConstructor() { + assertThat(UnaryFieldAction.class, hasValidBeanConstructor()); + } + + @Test + public void checkHashCodeFor() { + assertThat(UnaryFieldAction.class, hasValidBeanHashCodeFor()); + } + + @Test + public void unaryFieldActionBaseFieldTest() { + UnaryFieldAction unaryFieldAction = buildBaseUnaryFieldAction(); + unaryFieldAction.regexState(STATE); + + assertEquals(STATE, unaryFieldAction.regexState()); + assertNotNull(unaryFieldAction.getFrom()); + + UnaryFieldAction.From from = unaryFieldAction.getFrom(); + assertEquals(REGEX, from.getRegex()); + assertEquals(VALUE_STRING_MAIN, from.getValue()); + assertNotNull(from.getValues()); + + BaseAction.FromField field = from.getValues().get(0); + assertEquals(VALUE_STRING_INSIDE, field.getValue()); + + List fromValues = unaryFieldAction.fromValues(); + assertTrue(fromValues.contains(VALUE_STRING_INSIDE)); + } + + private UnaryFieldAction buildBaseUnaryFieldAction() { + String unaryField = "{from:{regex:" + REGEX + "," + "value:" + VALUE_STRING_MAIN + "," + + "values:" + VALUE_ARRAY + "}}"; + return ActionBuilder.buildAction(unaryField, UnaryFieldAction.class); + } +} -- cgit 1.2.3-korg