diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /config/src/test |
Publish
Diffstat (limited to 'config/src/test')
112 files changed, 9283 insertions, 0 deletions
diff --git a/config/src/test/java/com/yahoo/config/subscription/AppService.java b/config/src/test/java/com/yahoo/config/subscription/AppService.java new file mode 100644 index 00000000000..1f0fc43ed4e --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/AppService.java @@ -0,0 +1,76 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.foo.AppConfig; +import com.yahoo.vespa.config.TimingValues; + +/** + * @author <a href="musum@yahoo-inc.com">Harald Musum</a> + * + * Application that subscribes to config defined in app.def and + * generated code in AppConfig.java. + */ +public class AppService { + protected int timesConfigured = 0; + + protected AppConfig config = null; + private final ConfigSubscriber subscriber; + protected final String configId; + + final Thread configThread; + boolean stopThread = false; + + public AppService(String configId, ConfigSourceSet csource) { + this(configId, csource, null); + } + + public int timesConfigured() { return timesConfigured; } + + public AppService(String configId, ConfigSourceSet csource, TimingValues timingValues) { + if (csource == null) throw new IllegalArgumentException("Config source cannot be null"); + this.configId = configId; + subscriber = new ConfigSubscriber(csource); + ConfigHandle<AppConfig> temp; + if (timingValues == null) { + temp = subscriber.subscribe(AppConfig.class, configId); + } else { + temp = subscriber.subscribe(AppConfig.class, configId, csource, timingValues); + } + final ConfigHandle<AppConfig> handle = temp; + configThread = new Thread(new Runnable() { + @Override + public void run() { + while (!stopThread) { + boolean changed = subscriber.nextConfig(500); + if (changed) { + configure(handle.getConfig()); + timesConfigured++; + } + } + } + }); + subscriber.nextConfig(5000); + timesConfigured++; + configure(handle.getConfig()); + configThread.setDaemon(true); + configThread.start(); + } + + public void configure(AppConfig config) { + this.config = config; + } + + public void cancelSubscription() { + subscriber.close(); + stopThread = true; + } + + public AppConfig getConfig() { + return config; + } + + public boolean isConfigured() { + return (timesConfigured > 0); + } + +} diff --git a/config/src/test/java/com/yahoo/config/subscription/BasicTest.java b/config/src/test/java/com/yahoo/config/subscription/BasicTest.java new file mode 100644 index 00000000000..0d59e9e7ee7 --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/BasicTest.java @@ -0,0 +1,31 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.is; + +import com.yahoo.foo.AppConfig; + +import org.junit.Test; + + +public class BasicTest { + + @Test + public void testSubBasic() { + ConfigSubscriber s = new ConfigSubscriber(); + ConfigHandle<AppConfig> h = s.subscribe(AppConfig.class, "raw:times 0"); + s.nextConfig(0); + AppConfig c = h.getConfig(); + assertThat(c.times(), is(0)); + } + + @Test + public void testSubBasicGeneration() { + ConfigSubscriber s = new ConfigSubscriber(); + ConfigHandle<AppConfig> h = s.subscribe(AppConfig.class, "raw:times 2"); + s.nextGeneration(0); + AppConfig c = h.getConfig(); + assertThat(c.times(), is(2)); + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/CfgConfigPayloadBuilderTest.java b/config/src/test/java/com/yahoo/config/subscription/CfgConfigPayloadBuilderTest.java new file mode 100644 index 00000000000..55d1e5774a5 --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/CfgConfigPayloadBuilderTest.java @@ -0,0 +1,315 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.config.ConfigInstance; +import com.yahoo.foo.FunctionTestConfig; +import com.yahoo.config.InnerNode; +import com.yahoo.foo.SimpletypesConfig; +import com.yahoo.vespa.config.ConfigPayload; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static com.yahoo.config.subscription.util.JsonHelper.assertJsonEquals; +import static com.yahoo.config.subscription.util.JsonHelper.inputJson; +import static org.junit.Assert.assertEquals; + +/** + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @author Vegard Sjonfjell + * @since 5.1 + */ +public class CfgConfigPayloadBuilderTest { + + @Test + public void createConfigPayload() { + final FunctionTestConfig config = ConfigInstancePayloadTest.createVariableAccessConfigWithBuilder(); + + final String expectedJson = inputJson( + "{", + " 'double_val': '41.23',", + " 'refarr': [", + " ':parent:',", + " ':parent',", + " 'parent:'", + " ],", + " 'pathVal': 'src/test/resources/configs/def-files/function-test.def',", + " 'string_val': 'foo',", + " 'myStructMap': {", + " 'one': {", + " 'myString': 'bull',", + " 'anotherMap': {", + " 'anotherOne': {", + " 'anInt': '3',", + " 'anIntDef': '4'", + " }", + " },", + " 'myInt': '1',", + " 'myStringDef': 'bear',", + " 'myIntDef': '2'", + " }", + " },", + " 'boolarr': [", + " 'false'", + " ],", + " 'intMap': {", + " 'dotted.key': '3',", + " 'spaced key': '4',", + " 'two': '2',", + " 'one': '1'", + " },", + " 'int_val': '5',", + " 'stringarr': [", + " 'bar'", + " ],", + " 'enum_val': 'FOOBAR',", + " 'myarray': [", + " {", + " 'anotherarray': [", + " {", + " 'foo': '7'", + " }", + " ],", + " 'intval': '-5',", + " 'fileVal': 'file0',", + " 'refval': ':parent:',", + " 'myStruct': {", + " 'a': '1',", + " 'b': '2'", + " },", + " 'stringval': [", + " 'baah',", + " 'yikes'", + " ],", + " 'enumval': 'INNER'", + " },", + " {", + " 'anotherarray': [", + " {", + " 'foo': '2'", + " }", + " ],", + " 'intval': '5',", + " 'fileVal': 'file1',", + " 'refval': ':parent:',", + " 'myStruct': {", + " 'a': '-1',", + " 'b': '-2'", + " },", + " 'enumval': 'INNER'", + " }", + " ],", + " 'fileArr': [", + " 'bin'", + " ],", + " 'enumwithdef': 'BAR2',", + " 'bool_with_def': 'true',", + " 'enumarr': [", + " 'VALUES'", + " ],", + " 'pathMap': {", + " 'one': 'pom.xml'", + " },", + " 'long_with_def': '-9876543210',", + " 'double_with_def': '-12.0',", + " 'stringMap': {", + " 'double spaced key': 'third',", + " 'double.dotted.key': 'second',", + " 'one': 'first'", + " },", + " 'refwithdef': ':parent:',", + " 'stringwithdef': 'bar and foo',", + " 'doublearr': [", + " '2344.0',", + " '123.0'", + " ],", + " 'int_with_def': '-14',", + " 'pathArr': [", + " 'pom.xml'", + " ],", + " 'rootStruct': {", + " 'innerArr': [", + " {", + " 'stringVal': 'deep',", + " 'boolVal': 'true'", + " },", + " {", + " 'stringVal': 'blue a=\\\'escaped\\\'',", + " 'boolVal': 'false'", + " }", + " ],", + " 'inner0': {", + " 'index': '11',", + " 'name': 'inner0'", + " },", + " 'inner1': {", + " 'index': '12',", + " 'name': 'inner1'", + " }", + " },", + " 'fileVal': 'etc',", + " 'refval': ':parent:',", + " 'onechoice': 'ONLYFOO',", + " 'bool_val': 'false',", + " 'longarr': [", + " '9223372036854775807',", + " '-9223372036854775808'", + " ],", + " 'basicStruct': {", + " 'intArr': [", + " '310',", + " '311'", + " ],", + " 'foo': 'basicFoo',", + " 'bar': '3'", + " },", + " 'long_val': '12345678901'", + "}" + ); + + CfgConfigPayloadBuilderTest.assertDeserializedConfigEqualsJson(config, expectedJson); + } + + // FIXME: We need to define the behavior here. + @Test + public void test_empty_struct_arrays() { + assertDeserializedConfigEqualsJson("myArray[1]", inputJson("{}")); + } + + @Test + public void test_escaped_string() { + assertDeserializedConfigEqualsJson("a b=\"escaped\"", + inputJson( + "{", + " 'a': 'b=\\\'escaped\\\''", + "}" + ) + ); + } + + @Test + public void test_empty_payload() { + assertDeserializedConfigEqualsJson("", inputJson("{}")); + } + + @Test + public void test_leading_whitespace() { + assertDeserializedConfigEqualsJson(" a 0", + inputJson( + "{", + " 'a': '0'", + "}") + ); + } + + @Test + public void test_leading_and_trailing_whitespace_string() { + assertDeserializedConfigEqualsJson( + "a \" foo \"", + inputJson( + "{", + " 'a': ' foo '", + "}")); + } + + @Test + public void test_config_with_comments() { + CfgConfigPayloadBuilderTest.assertDeserializedConfigEqualsJson( + Arrays.asList( + "fielda b\n", + "#fielda c\n", + "#fieldb c\n", + "# just a comment"), + inputJson( + "{", + " 'fielda': 'b'", + "}") + ); + } + + @Test + public void testConvertingMaps() { + List<String> payload = Arrays.asList( + "intmap{\"foo\"} 1337", + "complexmap{\"key\"}.foo 1337", + "nestedmap{\"key1\"}.foo{\"key2\"}.bar 1337" + ); + + final String expectedJson = inputJson( + "{", + " 'nestedmap': {", + " 'key1': {", + " 'foo': {", + " 'key2': {", + " 'bar': '1337'", + " }", + " }", + " }", + " },", + " 'intmap': {", + " 'foo': '1337'", + " },", + " 'complexmap': {", + " 'key': {", + " 'foo': '1337'", + " }", + " }", + "}" + ); + + CfgConfigPayloadBuilderTest.assertDeserializedConfigEqualsJson(payload, expectedJson); + } + + @Test + public void createConfigPayloadUtf8() { + SimpletypesConfig.Builder builder = new SimpletypesConfig.Builder().stringval("Hei \u00E6\u00F8\u00E5 \uBC14\uB451 \u00C6\u00D8\u00C5 hallo"); + SimpletypesConfig config = new SimpletypesConfig(builder); + + String expectedJson = inputJson( + "{", + " 'longval': '0',", + " 'intval': '0',", + " 'stringval': 'Hei \u00e6\u00f8\u00e5 \ubc14\ub451 \u00c6\u00d8\u00c5 hallo',", + " 'boolval': 'false',", + " 'doubleval': '0.0',", + " 'enumval': 'VAL1'", + "}" + ); + + CfgConfigPayloadBuilderTest.assertDeserializedConfigEqualsJson(config, expectedJson); + } + + @Test + public void testLineParsing() { + CfgConfigPayloadBuilder builder = new CfgConfigPayloadBuilder(); + assertEquals(builder.parseFieldAndValue("foo bar").getFirst(), "foo"); + assertEquals(builder.parseFieldAndValue("foo bar").getSecond(), "bar"); + assertEquals(builder.parseFieldAndValue("foo.bar.baz{my key} baR").getFirst(), "foo.bar.baz{my key}"); + assertEquals(builder.parseFieldAndValue("foo.bar.baz{my key} baR").getSecond(), "baR"); + assertEquals(builder.parseFieldAndValue("foo.bar.baz{my.key} baRR").getFirst(), "foo.bar.baz{my.key}"); + assertEquals(builder.parseFieldAndValue("foo.bar.baz{my.key} baRR").getSecond(), "baRR"); + assertEquals(builder.parseFieldAndValue("foo.bar.baz{my key.dotted}.biz baRO").getFirst(), "foo.bar.baz{my key.dotted}.biz"); + assertEquals(builder.parseFieldAndValue("foo.bar.baz{my key.dotted}.biz baRO").getSecond(), "baRO"); + + assertEquals(builder.parseFieldList("foo.bar.baz").get(0), "foo"); + assertEquals(builder.parseFieldList("foo.bar.baz").get(1), "bar"); + assertEquals(builder.parseFieldList("foo.bar.baz").get(2), "baz"); + assertEquals(builder.parseFieldList("foo.bar{f.b}.baz{f.h h}").get(0), "foo"); + assertEquals(builder.parseFieldList("foo.bar{f.b}.baz{f.h h}").get(1), "bar{f.b}"); + assertEquals(builder.parseFieldList("foo.bar{f.b}.baz{f.h h}").get(2), "baz{f.h h}"); + } + + private static void assertDeserializedConfigEqualsJson(InnerNode config, String expectedJson) { + assertDeserializedConfigEqualsJson(ConfigInstance.serialize(config), expectedJson); + } + + private static void assertDeserializedConfigEqualsJson(String serializedConfig, String expectedJson) { + assertDeserializedConfigEqualsJson(Arrays.asList(serializedConfig), expectedJson); + } + + private static void assertDeserializedConfigEqualsJson(List<String> inputConfig, String expectedJson) { + ConfigPayload payload = new CfgConfigPayloadBuilder().deserialize(inputConfig); + assertJsonEquals(payload.toString(), expectedJson); + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigApiTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigApiTest.java new file mode 100755 index 00000000000..608bf2e12b0 --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigApiTest.java @@ -0,0 +1,60 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.config.ConfigInstance; +import com.yahoo.foo.*; + +import org.junit.Test; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.is; + +/** + * Tests ConfigSubscriber API, and the ConfigHandle class. + * + * @author <a href="gv@yahoo-inc.com">Harald Musum</a> + * @since 5.1 + */ +public class ConfigApiTest { + private static final String CONFIG_ID = "raw:" + "times 1\n"; + + @Test + public void testConfigSubscriber() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + ConfigHandle<AppConfig> h = subscriber.subscribe(AppConfig.class, CONFIG_ID); + assertNotNull(h); + subscriber.nextConfig(); + assertNotNull(h.getConfig()); + assertEquals(AppConfig.CONFIG_DEF_NAME, ConfigInstance.getDefName(h.getConfig().getClass())); + assertThat(h.isChanged(), is(true)); + assertTrue(h.toString().startsWith("Handle changed: true\nSub:\n")); + subscriber.close(); + assertThat(subscriber.state(), is(ConfigSubscriber.State.CLOSED)); + } + + /** + * Verifies that we get an exception when trying to subscribe after close() has been called + * for a ConfigSubscriber + */ + @Test(expected = IllegalStateException.class) + public void testSubscribeAfterClose() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + subscriber.subscribe(AppConfig.class, CONFIG_ID); + subscriber.nextConfig(); + subscriber.close(); + subscriber.subscribe(AppConfig.class, CONFIG_ID); + } + + /** + * Verifies that it is not possible to to subscribe again after calling nextConfig() + */ + @Test(expected = IllegalStateException.class) + public void testSubscribeAfterNextConfig() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + subscriber.subscribe(AppConfig.class, CONFIG_ID); + subscriber.nextConfig(); + subscriber.subscribe(AppConfig.class, CONFIG_ID); + subscriber.close(); + } + +} diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigGetterTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigGetterTest.java new file mode 100644 index 00000000000..bb5e712fe3a --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigGetterTest.java @@ -0,0 +1,111 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.foo.AppConfig; + +import org.junit.Test; + +import java.io.File; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * Unit test for the {@link ConfigGetter}. + * + * @author gjoranv + */ +public class ConfigGetterTest { + private ConfigSourceSet sourceSet = new ConfigSourceSet("config-getter-test"); + + @Test + public void testGetConfig() { + int times = 11; + String message = "testGetConfig"; + String a0 = "a0"; + String configId = "raw:times " + times + "\nmessage " + message + "\na[1]\na[0].name " + a0; + + ConfigGetter<AppConfig> getter = new ConfigGetter<>(AppConfig.class); + AppConfig config = getter.getConfig(configId); + assertThat(config.times(), is(times)); + assertThat(config.message(), is(message)); + assertThat(config.a().size(), is(1)); + assertThat(config.a(0).name(), is(a0)); + + AppService service = new AppService(configId, sourceSet); + AppConfig serviceConfig = service.getConfig(); + assertTrue(service.isConfigured()); + assertThat(config, is(serviceConfig)); + } + +@Test + public void testGetFromRawSource() { + ConfigGetter<AppConfig> getter = new ConfigGetter<>(new RawSource("message \"one\""), AppConfig.class); + AppConfig config = getter.getConfig("test"); + assertThat(config.message(), is("one")); + } + + @Test + public void testGetTwice() { + ConfigGetter<AppConfig> getter = new ConfigGetter<>(AppConfig.class); + AppConfig config = getter.getConfig("raw:message \"one\""); + assertThat(config.message(), is("one")); + config = getter.getConfig("raw:message \"two\""); + assertThat(config.message(), is("two")); + } + + @Test + public void testGetFromFile() { + ConfigGetter<AppConfig> getter = new ConfigGetter<>(AppConfig.class); + AppConfig config = getter.getConfig("file:src/test/resources/configs/foo/app.cfg"); + verifyFooValues(config); + } + + @Test + public void testGetFromFileSource() { + ConfigGetter<AppConfig> getter = new ConfigGetter<>(new FileSource(new File("src/test/resources/configs/foo/app.cfg")), AppConfig.class); + AppConfig config = getter.getConfig("test"); + verifyFooValues(config); + } + + @Test + public void testGetFromDir() { + ConfigGetter<AppConfig> getter = new ConfigGetter<>(AppConfig.class); + AppConfig config = getter.getConfig("dir:src/test/resources/configs/foo/"); + verifyFooValues(config); + } + + @Test + public void testGetFromDirSource() { + AppConfig config = ConfigGetter.getConfig(AppConfig.class, "test", new DirSource(new File("src/test/resources/configs/foo/"))); + verifyFooValues(config); + } + + private void verifyFooValues(AppConfig config) { + assertThat(config.message(), is("msg1")); + assertThat(config.times(), is(3)); + assertThat(config.a(0).name(), is("a0")); + assertThat(config.a(1).name(), is("a1")); + assertThat(config.a(2).name(), is("a2")); + } + + @Test + public void testsStaticGetConfig() { + int times = 11; + String message = "testGetConfig"; + String a0 = "a0"; + String configId = "raw:times " + times + "\nmessage " + message + "\na[1]\na[0].name " + a0; + + AppConfig config = ConfigGetter.getConfig(AppConfig.class, configId); + assertThat(config.times(), is(times)); + assertThat(config.message(), is(message)); + assertThat(config.a().size(), is(1)); + assertThat(config.a(0).name(), is(a0)); + + AppService service = new AppService(configId, sourceSet); + AppConfig serviceConfig = service.getConfig(); + assertTrue(service.isConfigured()); + assertThat(config, is(serviceConfig)); + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigInstancePayloadTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigInstancePayloadTest.java new file mode 100644 index 00000000000..d7a88d591e4 --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigInstancePayloadTest.java @@ -0,0 +1,188 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.config.ConfigInstance; +import com.yahoo.config.FileReference; +import com.yahoo.foo.FunctionTestConfig; +import com.yahoo.foo.MaptypesConfig; +import com.yahoo.slime.Slime; +import com.yahoo.vespa.config.ConfigPayload; +import com.yahoo.vespa.config.ConfigTransformer; +import org.junit.Test; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import static com.yahoo.foo.FunctionTestConfig.*; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +/** + * @author gjoranv + * @since 5.1.6 + */ +public class ConfigInstancePayloadTest { + + static FunctionTestConfig createVariableAccessConfigWithBuilder() { + return new FunctionTestConfig(createVariableAccessBuilder()); + } + + static FunctionTestConfig.Builder createVariableAccessBuilder() { + return new FunctionTestConfig.Builder(). + bool_val(false). + bool_with_def(true). + int_val(5). + int_with_def(-14). + long_val(12345678901L). + long_with_def(-9876543210L). + double_val(41.23). + double_with_def(-12). + string_val("foo"). + stringwithdef("bar and foo"). + enum_val(Enum_val.FOOBAR). + enumwithdef(Enumwithdef.BAR2). + refval(":parent:"). + refwithdef(":parent:"). + fileVal("etc"). + pathVal(FileReference.mockFileReferenceForUnitTesting(new File("src/test/resources/configs/def-files/function-test.def"))). + boolarr(false). + longarr(9223372036854775807L). + longarr(-9223372036854775808L). + doublearr(2344.0). + doublearr(123.0). + stringarr("bar"). + enumarr(Enumarr.VALUES). + refarr(Arrays.asList(":parent:", ":parent", "parent:")). // test collection based setter + fileArr("bin"). + pathArr(FileReference.mockFileReferenceForUnitTesting(new File("pom.xml"))). + intMap("one", 1). + intMap("two", 2). + intMap("dotted.key", 3). + intMap("spaced key", 4). + stringMap("one", "first"). + stringMap("double.dotted.key", "second"). + stringMap("double spaced key", "third"). + pathMap("one", FileReference.mockFileReferenceForUnitTesting(new File("pom.xml"))). + basicStruct(new BasicStruct.Builder(). + foo("basicFoo"). + bar(3). + intArr(310).intArr(311)). + + rootStruct(new RootStruct.Builder(). + inner0(new RootStruct.Inner0.Builder(). + index(11)). + inner1(new RootStruct.Inner1.Builder(). + index(12)). + innerArr(new RootStruct.InnerArr.Builder(). + boolVal(true). + stringVal("deep")). + innerArr(new RootStruct.InnerArr.Builder(). + boolVal(false). + stringVal("blue a=\"escaped\""))). + + myarray(new Myarray.Builder(). + intval(-5). + stringval("baah"). + stringval("yikes"). + enumval(Myarray.Enumval.INNER). + refval(":parent:"). + fileVal("file0"). + anotherarray(new Myarray.Anotherarray.Builder(). + foo(7)). + myStruct(new Myarray.MyStruct.Builder(). + a(1). + b(2))). + + myarray(new Myarray.Builder(). + intval(5). + enumval(Myarray.Enumval.INNER). + refval(":parent:"). + fileVal("file1"). + anotherarray(new Myarray.Anotherarray.Builder(). + foo(1). + foo(2)). + myStruct(new Myarray.MyStruct.Builder(). + a(-1). + b(-2))). + + myStructMap("one", new MyStructMap.Builder(). + myInt(1). + myString("bull"). + myIntDef(2). + myStringDef("bear"). + anotherMap("anotherOne", new MyStructMap.AnotherMap.Builder(). + anInt(3). + anIntDef(4))); + } + + @Test + public void config_builder_can_be_created_from_generic_payload() { + FunctionTestConfig config = createVariableAccessConfigWithBuilder(); + ConfigPayload payload = new CfgConfigPayloadBuilder().deserialize(ConfigInstance.serialize(config)); + assertFunctionTestPayload(config, payload); + } + + @Test + public void config_builder_can_be_created_from_typed_payload() { + FunctionTestConfig config = createVariableAccessConfigWithBuilder(); + Slime slime = new Slime(); + ConfigInstanceSerializer serializer = new ConfigInstanceSerializer(slime); + ConfigInstance.serialize(config, serializer); + assertFunctionTestPayload(config, new ConfigPayload(slime)); + } + + private void assertFunctionTestPayload(FunctionTestConfig expected, ConfigPayload payload) { + try { + System.out.println(payload.toString(false)); + FunctionTestConfig config2 = new FunctionTestConfig((FunctionTestConfig.Builder)new ConfigTransformer<>(FunctionTestConfig.class).toConfigBuilder(payload)); + assertThat(config2, is(expected)); + assertThat(ConfigInstance.serialize(config2), is(ConfigInstance.serialize(expected))); + } catch (Exception e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void function_test_payload_is_correctly_deserialized() { + FunctionTestConfig orig = createVariableAccessConfigWithBuilder(); + List<String> lines = ConfigInstance.serialize(orig); + ConfigPayload payload = new CfgConfigPayloadBuilder().deserialize(lines); + FunctionTestConfig config = ConfigInstanceUtil.getNewInstance(FunctionTestConfig.class, "foo", payload); + FunctionTest.assertVariableAccessValues(config, "foo"); + } + + @Test + public void map_types_are_correctly_deserialized() { + MaptypesConfig orig = createMapTypesConfig(); + List<String> lines = ConfigInstance.serialize(orig); + ConfigPayload payload = new CfgConfigPayloadBuilder().deserialize(lines); + System.out.println(payload.toString()); + MaptypesConfig config = ConfigInstanceUtil.getNewInstance(MaptypesConfig.class, "foo", payload); + System.out.println(config); + assertThat(config.intmap().size(), is(1)); + assertThat(config.intmap("foo"), is(1337)); + assertNotNull(config.innermap("bar")); + assertThat(config.innermap("bar").foo(), is(93)); + assertThat(config.nestedmap().size(), is(1)); + assertNotNull(config.nestedmap("baz")); + assertThat(config.nestedmap("baz").inner("foo"), is(1)); + assertThat(config.nestedmap("baz").inner("bar"), is(2)); + } + + private MaptypesConfig createMapTypesConfig() { + MaptypesConfig.Builder builder = new MaptypesConfig.Builder(); + builder.intmap("foo", 1337); + MaptypesConfig.Innermap.Builder inner = new MaptypesConfig.Innermap.Builder(); + inner.foo(93); + builder.innermap("bar", inner); + MaptypesConfig.Nestedmap.Builder n1 = new MaptypesConfig.Nestedmap.Builder(); + n1.inner("foo", 1); + n1.inner("bar", 2); + builder.nestedmap("baz", n1); + return new MaptypesConfig(builder); + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializationTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializationTest.java new file mode 100644 index 00000000000..97e954b95fb --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializationTest.java @@ -0,0 +1,257 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.config.ConfigInstance; +import com.yahoo.foo.FunctionTestConfig; +import com.yahoo.config.codegen.DefLine; +import com.yahoo.vespa.config.ConfigPayload; +import org.junit.Test; + +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author gjoranv + * @author vegardh + */ +public class ConfigInstanceSerializationTest { + + private DefLine.Type stringType = new DefLine.Type("string"); + private DefLine.Type intType = new DefLine.Type("int"); + private DefLine.Type longType = new DefLine.Type("long"); + private DefLine.Type boolType = new DefLine.Type("bool"); + private DefLine.Type doubleType = new DefLine.Type("double"); + private DefLine.Type fileType = new DefLine.Type("file"); + private DefLine.Type refType = new DefLine.Type("reference"); + + @Test + public void require_symmetrical_serialization_and_deserialization_with_builder() { + FunctionTestConfig config = ConfigInstancePayloadTest.createVariableAccessConfigWithBuilder(); + + // NOTE: configId must be ':parent:' because the library replaces ReferenceNodes with that value with + // the instance's configId. (And the config used here contains such nodes.) + List<String> lines = ConfigInstance.serialize(config); + ConfigPayload payload = new CfgConfigPayloadBuilder().deserialize(lines); + + FunctionTestConfig config2 = ConfigInstanceUtil.getNewInstance(FunctionTestConfig.class, ":parent:", payload); + assertThat(config, is(config2)); + assertThat(ConfigInstance.serialize(config), is(ConfigInstance.serialize(config2))); + } + +/** Looks like everything in the commented block tests unused api's.. remove? + + @Test + public void testSerializeAgainstConfigDefinitionAllowNothing() { + FunctionTestConfig config = ConfigInstancePayloadTest.createVariableAccessConfigWithBuilder(); + InnerCNode def = new InnerCNode("function-test"); + List<String> payload = Validator.serialize(config, def); + Assert.assertEquals(payload.size(), 0); + } + + @Test + public void testSerializeAgainstConfigDefinitionAllLeaves() { + FunctionTestConfig config = ConfigInstancePayloadTest.createVariableAccessConfigWithBuilder(); + InnerCNode def = new InnerCNode("function-test"); + def.children().put("bool_val", LeafCNode.newInstance(boolType, def, "bool_val")); + def.children().put("bool_with_def", LeafCNode.newInstance(boolType, def, "bool_with_def")); + def.children().put("int_val", LeafCNode.newInstance(intType, def, "int_val")); + def.children().put("int_with_def", LeafCNode.newInstance(intType, def, "int_with_def")); + def.children().put("long_val", LeafCNode.newInstance(longType, def, "long_val")); + def.children().put("long_with_def", LeafCNode.newInstance(longType, def, "long_with_def")); + def.children().put("double_val", LeafCNode.newInstance(doubleType, def, "double_val")); + def.children().put("double_with_def", LeafCNode.newInstance(doubleType, def, "double_with_def")); + def.children().put("string_val", LeafCNode.newInstance(stringType, def, "string_val")); + def.children().put("stringwithdef", LeafCNode.newInstance(stringType, def, "stringwithdef")); + def.children().put("enum_val", LeafCNode.newInstance(enumType(new String[]{"FOO", "BAR", "FOOBAR"}), def, "enum_val")); + def.children().put("enumwithdef", LeafCNode.newInstance(enumType(new String[]{"FOO2", "BAR2", "FOOBAR2"}), def, "enumwithdef", "BAR2")); + def.children().put("refval", LeafCNode.newInstance(refType, def, "refval")); + def.children().put("refwithdef", LeafCNode.newInstance(refType, def, "refwithdef")); + def.children().put("fileVal", LeafCNode.newInstance(fileType, def, "fileVal")); + + List<String> payload = Validator.serialize(config, def); + String plString = payload.toString(); + Assert.assertTrue(plString.matches(".*bool_val false.*")); + Assert.assertTrue(plString.matches(".*bool_with_def true.*")); + Assert.assertTrue(plString.matches(".*int_val 5.*")); + Assert.assertTrue(plString.matches(".*int_with_def -14.*")); + Assert.assertTrue(plString.matches(".*long_val 12345678901.*")); + Assert.assertTrue(plString.matches(".*long_with_def -9876543210.*")); + Assert.assertTrue(plString.matches(".*double_val 41\\.23.*")); + Assert.assertTrue(plString.matches(".*double_with_def -12.*")); + Assert.assertTrue(plString.matches(".*string_val \"foo\".*")); + Assert.assertTrue(plString.matches(".*stringwithdef \"bar and foo\".*")); + Assert.assertTrue(plString.matches(".*enum_val FOOBAR.*")); + Assert.assertTrue(plString.matches(".*enumwithdef BAR2.*")); + Assert.assertTrue(plString.matches(".*refval \\:parent\\:.*")); + Assert.assertTrue(plString.matches(".*refwithdef \\:parent\\:.*")); + Assert.assertTrue(plString.matches(".*fileVal \"etc\".*")); + } + + @Test + public void testSerializeAgainstConfigDefinitionSomeLeaves() { + FunctionTestConfig config = ConfigInstancePayloadTest.createVariableAccessConfigWithBuilder(); + InnerCNode def = new InnerCNode("function-test"); + def.children().put("stringwithdef", LeafCNode.newInstance(stringType, def, "stringwithdef")); + def.children().put("long_with_def", LeafCNode.newInstance(longType, def, "long_with_def")); + // But not double_with_def etc, and no structs/arrays + List<String> payload = Validator.serialize(config, def); + String plString = payload.toString(); + Assert.assertTrue(plString.matches(".*long_with_def \\-9876543210.*")); + Assert.assertTrue(plString.matches(".*stringwithdef \"bar and foo\".*")); + Assert.assertFalse(plString.matches(".*double_with_def.*")); + Assert.assertFalse(plString.matches(".*fileVal \"etc\".*")); + Assert.assertFalse(plString.matches(".*basicStruct.*")); + } + + @Test + public void testSerializationAgainstConfigDefinitionAddedValsInDef() { + FunctionTestConfig config = ConfigInstancePayloadTest.createVariableAccessConfigWithBuilder(); + InnerCNode def = new InnerCNode("function-test"); + def.children().put("stringwithdef", LeafCNode.newInstance(stringType, def, "stringwithdef")); + def.children().put("someotherstring", LeafCNode.newInstance(stringType, def, "someotherstring", "some other")); + def.children().put("long_with_def", LeafCNode.newInstance(longType, def, "long_with_def")); + def.children().put("some_other_long", LeafCNode.newInstance(longType, def, "some_other_long", "88")); + def.children().put("some_other_enum", LeafCNode.newInstance(enumType(new String[]{"hey", "ho", "lets", "go"}), def, "some_other_enum", "lets")); + def.children().put("int_val_nofdef", LeafCNode.newInstance(intType, def, "int_val_nodef", null)); + + // But not double_with_def etc, and no structs/arrays + List<String> payload = Validator.serialize(config, def); + String plString = payload.toString(); + Assert.assertTrue(plString.matches(".*long_with_def \\-9876543210.*")); + Assert.assertTrue(plString.matches(".*stringwithdef \"bar and foo\".*")); + Assert.assertTrue(plString.matches(".*.someotherstring \"some other\".*")); + Assert.assertTrue(plString.matches(".*some_other_long 88.*")); + Assert.assertTrue(plString.matches(".*some_other_enum lets.*")); + Assert.assertFalse(plString.matches(".*double_with_def.*")); + Assert.assertFalse(plString.matches(".*fileVal \"etc\".*")); + Assert.assertFalse(plString.matches(".*basicStruct.*")); + Assert.assertFalse(plString.matches(".*int_val_nodef.*")); + } + + @Test + public void testSerializeAgainstConfigDefinitionMismatchAllWays() { + FunctionTestConfig config = ConfigInstancePayloadTest.createVariableAccessConfigWithBuilder(); + + // Create all sorts of mismatches in the def schema used to serialize + InnerCNode def = new InnerCNode("function-test"); + def.children().put("long_with_def", LeafCNode.newInstance(longType, def, "long_with_def")); + def.children().put("stringwithdef", LeafCNode.newInstance(intType, def, "stringwithdef")); + def.children().put("basicStruct", LeafCNode.newInstance(intType, def, "basicStruct")); + InnerCNode doubleValWrong = new InnerCNode("double_val"); + doubleValWrong.children().put("foo", LeafCNode.newInstance(intType, def, "foo")); + def.children().put("double_val", doubleValWrong); + InnerCNode myArray = new InnerCNode("myarray"); + myArray.children().put("intval", LeafCNode.newInstance(stringType, myArray, "foo")); + InnerCNode myStruct = new InnerCNode("myStruct"); + myStruct.children().put("a", LeafCNode.newInstance(stringType, myStruct, "foo")); + myArray.children().put("myStruct", myStruct); + def.children().put("myarray", myArray); + + List<String> payload = Validator.serialize(config, def); + String plString = payload.toString(); + Assert.assertTrue(plString.matches(".*long_with_def.*")); + Assert.assertFalse(plString.matches(".*stringwithdef.*")); + Assert.assertFalse(plString.matches(".*basicStruct.*")); + Assert.assertFalse(plString.matches(".*double_val.*")); + Assert.assertFalse(plString.matches(".*intval.*")); + Assert.assertFalse(plString.matches(".*\\.a.*")); + } + + @Test + public void testSerializeAgainstConfigDefinitionComplex() { + FunctionTestConfig config = ConfigInstancePayloadTest.createVariableAccessConfigWithBuilder(); + + // Build a pretty complex def programatically + InnerCNode def = new InnerCNode("function-test"); + def.children().put("stringwithdef", LeafCNode.newInstance(stringType, def, "stringwithdef")); + def.children().put("someUnknownStringNoDefault", LeafCNode.newInstance(stringType, def, "someUnknownStringNoDefault")); + InnerCNode basicStruct = new InnerCNode("basicStruct"); + basicStruct.children().put("foo", LeafCNode.newInstance(stringType, def, "foo")); // but not bar + InnerCNode rootStruct = new InnerCNode("rootStruct"); + InnerCNode inner1 = new InnerCNode("inner1"); + InnerCNode someUnknwonStruct = new InnerCNode("someUnknownStruct"); + InnerCNode someUnknownInner = new InnerCNode("someUnknownInner"); + InnerCNode innerArr = new InnerCNode("innerArr"); + rootStruct.children().put("inner1", inner1); + rootStruct.children().put("someUnknownStruct", someUnknwonStruct); + rootStruct.children().put("someUnknownInner", someUnknownInner); + rootStruct.children().put("innerArr", innerArr); + InnerCNode myarray = new InnerCNode("myarray"); + InnerCNode unknownInner = new InnerCNode("unknownInner"); + def.children().put("basicStruct", basicStruct); + def.children().put("rootStruct", rootStruct); + def.children().put("myarray", myarray); + def.children().put("unknownInner", unknownInner); + inner1.children().put("index", LeafCNode.newInstance(intType, inner1, "index")); + inner1.children().put("someUnknownInt", LeafCNode.newInstance(intType, inner1, "someUnknownInt", "-98")); + inner1.children().put("someUnknownIntNoDefault", LeafCNode.newInstance(intType, inner1, "someUnknownIntNoDefault")); + inner1.children().put("someUnknownEnum", LeafCNode.newInstance(enumType(new String[]{"goo", "go", "gorilla"}), inner1, "someUnknownEnum", "go")); + inner1.children().put("someUnknownEnumNoDefault", LeafCNode.newInstance(enumType(new String[]{"foo", "bar", "baz"}), inner1, "someUnknownEnumNoDefault")); + someUnknwonStruct.children().put("anint", LeafCNode.newInstance(intType, someUnknwonStruct, "anint", "3"));// But no instances of this in config + someUnknownInner.children().put("along", LeafCNode.newInstance(longType, someUnknownInner, "along", "234"));// No instance in config + innerArr.children().put("boolVal", LeafCNode.newInstance(boolType, innerArr, "boolVal")); + innerArr.children().put("someUnknownDouble", LeafCNode.newInstance(doubleType, innerArr, "someUnknownDouble", "-675.789")); + innerArr.children().put("someUnknownDoubleNoDefault", LeafCNode.newInstance(doubleType, innerArr, "someUnknownDoubleNoDefault")); + myarray.children().put("fileVal", LeafCNode.newInstance(fileType, myarray, "fileVal")); + myarray.children().put("stringval", new InnerCNode("stringval[]")); + // TODO make sure default for file is not allowed + //myarray.children().put("someUnknownFile", LeafCNode.newInstance(fileType, myarray, "someUnknownFile", "opt/")); + unknownInner.children().put("aDouble", LeafCNode.newInstance(doubleType, unknownInner, "aDouble", "1234")); + def.children().put("longarr", new InnerCNode("longarr[]")); + def.children().put("boolarr", new InnerCNode("boolarr[]")); + def.children().put("doublearr", new InnerCNode("doublearr[]")); + def.children().put("stringarr", new InnerCNode("stringarr[]")); + def.children().put("fileArr", new InnerCNode("fileArr[]")); + def.children().put("refarr", new InnerCNode("refarr[]")); + def.children().put("enumarr", new InnerCNode("enumarr[]")); + List<String> payload = Validator.serialize(config, def); + String plString = payload.toString(); + Assert.assertFalse(plString.matches(".*long_with_def \\-9876543210.*")); + Assert.assertFalse(plString.matches(".*someUnknownStringNoDefault.*")); + Assert.assertTrue(plString.matches(".*stringwithdef \"bar and foo\".*")); + Assert.assertFalse(plString.matches(".*double_with_def.*")); + Assert.assertFalse(plString.matches(".*fileVal etc.*")); + Assert.assertTrue(plString.matches(".*basicStruct\\.foo \"basicFoo\".*")); + Assert.assertFalse(plString.matches(".*basicStruct\\.bar.*")); + Assert.assertFalse(plString.matches(".*rootStruct\\.inner0.*")); + Assert.assertFalse(plString.matches(".*unknownInner.*")); + Assert.assertFalse(plString.matches(".*rootStruct\\.someUnknownStruct.*")); + Assert.assertFalse(plString.matches(".*rootStruct\\.someUnknownInner.*")); + Assert.assertFalse(plString.matches(".*rootStruct\\.inner1\\.name.*")); + Assert.assertTrue(plString.matches(".*rootStruct\\.inner1\\.index 12.*")); + Assert.assertTrue(plString.matches(".*rootStruct\\.inner1\\.someUnknownInt -98.*")); + Assert.assertTrue(plString.matches(".*rootStruct\\.inner1\\.someUnknownEnum go.*")); + Assert.assertTrue(plString.matches(".*rootStruct\\.innerArr\\[0\\]\\.boolVal true.*")); + Assert.assertFalse(plString.matches(".*someUnknownEnumNoDefault.*")); + Assert.assertFalse(plString.matches(".*someUnknownDoubleNoDefault.*")); + Assert.assertFalse(plString.matches(".*someUnknownIntNoDefault.*")); + Assert.assertTrue(plString.matches(".*rootStruct\\.innerArr\\[0\\]\\.someUnknownDouble -675.789.*")); + Assert.assertFalse(plString.matches(".*rootStruct\\.innerArr\\[0\\]\\.stringVal*")); + Assert.assertFalse(plString.matches(".*myarray\\[0\\].intval.*")); + Assert.assertTrue(plString.matches(".*myarray\\[0\\].fileVal \"file0\".*")); + //assertTrue(plString.matches(".*myarray\\[0\\].someUnknownFile \"opt/\".*")); + Assert.assertTrue(plString.matches(".*myarray\\[0\\].stringval\\[0\\] \"baah\".*")); + Assert.assertTrue(plString.matches(".*myarray\\[0\\].stringval\\[1\\] \"yikes\".*")); + Assert.assertTrue(plString.matches(".*myarray\\[1\\].fileVal \"file1\".*")); + Assert.assertFalse(plString.matches(".*myarray\\[1\\].enumVal.*")); + Assert.assertFalse(plString.matches(".*myarray\\[1\\].refVal.*")); + Assert.assertTrue(plString.matches(".*boolarr\\[0\\] false.*")); + Assert.assertTrue(plString.matches(".*longarr\\[0\\] 9223372036854775807.*")); + Assert.assertTrue(plString.matches(".*longarr\\[1\\] -9223372036854775808.*")); + Assert.assertTrue(plString.matches(".*doublearr\\[0\\] 2344\\.0.*")); + Assert.assertTrue(plString.matches(".*doublearr\\[1\\] 123\\.0.*")); + Assert.assertTrue(plString.matches(".*stringarr\\[0\\] \"bar\".*")); + Assert.assertTrue(plString.matches(".*enumarr\\[0\\] VALUES.*")); + Assert.assertTrue(plString.matches(".*refarr\\[0\\] \\:parent\\:.*")); + Assert.assertTrue(plString.matches(".*refarr\\[1\\] \\:parent.*")); + Assert.assertTrue(plString.matches(".*refarr\\[2\\] parent\\:.*")); + Assert.assertTrue(plString.matches(".*fileArr\\[0\\] \"bin\".*")); + } + + private DefLine.Type enumType(String[] strings) { + return new DefLine.Type("enum").setEnumArray(strings); + } +**/ +} diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializerTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializerTest.java new file mode 100644 index 00000000000..d3713eaa401 --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializerTest.java @@ -0,0 +1,229 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.foo.ArraytypesConfig; +import com.yahoo.config.ConfigInstance; +import com.yahoo.foo.SimpletypesConfig; +import com.yahoo.foo.SpecialtypesConfig; +import com.yahoo.foo.StructtypesConfig; +import com.yahoo.foo.MaptypesConfig; +import com.yahoo.slime.JsonFormat; +import com.yahoo.slime.Slime; + +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import static com.yahoo.config.subscription.util.JsonHelper.assertJsonEquals; +import static com.yahoo.config.subscription.util.JsonHelper.inputJson; +import static org.junit.Assert.fail; + +/** + * @author lulf + * @author Vegard Sjonfjell + * @since 5.1 + */ +public class ConfigInstanceSerializerTest { + @Test + public void test_that_leaf_types_are_serialized_to_json_types() throws IOException { + SimpletypesConfig.Builder builder = new SimpletypesConfig.Builder(); + builder.boolval(false); + builder.stringval("foo"); + builder.intval(13); + builder.longval(14); + builder.doubleval(3.14); + builder.enumval(SimpletypesConfig.Enumval.Enum.VAL2); + + final SimpletypesConfig config = new SimpletypesConfig(builder); + final String expectedJson = inputJson( + "{", + " 'boolval': false,", + " 'doubleval': 3.14,", + " 'enumval': 'VAL2',", + " 'intval': 13,", + " 'longval': 14,", + " 'stringval': 'foo'", + "}" + ); + + assertConfigEquals(expectedJson, config); + } + + @Test + public void test_that_nested_structs_are_formatted_to_json() throws IOException { + StructtypesConfig.Builder builder = new StructtypesConfig.Builder(); + StructtypesConfig.Nested.Builder nestedBuilder = new StructtypesConfig.Nested.Builder(); + StructtypesConfig.Nested.Inner.Builder innerBuilder = new StructtypesConfig.Nested.Inner.Builder(); + innerBuilder.name("foo"); + innerBuilder.emails("lulf@foo"); + innerBuilder.emails("lulf@bar"); + innerBuilder.gender(StructtypesConfig.Nested.Inner.Gender.Enum.MALE); + nestedBuilder.inner(innerBuilder); + builder.nested(nestedBuilder); + StructtypesConfig.Nestedarr.Builder nestedArrBuilder = new StructtypesConfig.Nestedarr.Builder(); + StructtypesConfig.Nestedarr.Inner.Builder innerNestedArrBuilder = new StructtypesConfig.Nestedarr.Inner.Builder(); + innerNestedArrBuilder.emails("foo@bar"); + innerNestedArrBuilder.name("bar"); + innerNestedArrBuilder.gender(StructtypesConfig.Nestedarr.Inner.Gender.Enum.FEMALE); + nestedArrBuilder.inner(innerNestedArrBuilder); + builder.nestedarr(nestedArrBuilder); + + final StructtypesConfig config = new StructtypesConfig(builder); + final String expectedJson = inputJson( + "{", + " 'complexarr': [],", + " 'nested': {", + " 'inner': {", + " 'emails': [", + " 'lulf@foo',", + " 'lulf@bar'", + " ],", + " 'gender': 'MALE',", + " 'name': 'foo'", + " }", + " },", + " 'nestedarr': [", + " {", + " 'inner': {", + " 'emails': [", + " 'foo@bar'", + " ],", + " 'gender': 'FEMALE',", + " 'name': 'bar'", + " }", + " }", + " ],", + " 'simple': {", + " 'emails': [],", + " 'gender': 'MALE',", + " 'name': '_default_'", + " },", + " 'simplearr': []", + "}" + ); + + assertConfigEquals(expectedJson, config); + } + + @Test + public void test_that_arrays_are_formatted_to_json() throws IOException { + ArraytypesConfig.Builder builder = new ArraytypesConfig.Builder(); + builder.boolarr(true); + builder.boolarr(false); + builder.doublearr(1.2); + builder.doublearr(1.1); + builder.enumarr(ArraytypesConfig.Enumarr.Enum.VAL1); + builder.enumarr(ArraytypesConfig.Enumarr.Enum.VAL2); + builder.intarr(3); + builder.longarr(4l); + builder.stringarr("foo"); + + final ArraytypesConfig config = new ArraytypesConfig(builder); + final String expectedJson = inputJson( + "{", + " 'boolarr': [", + " true,", + " false", + " ],", + " 'doublearr': [", + " 1.2,", + " 1.1", + " ],", + " 'enumarr': [", + " 'VAL1',", + " 'VAL2'", + " ],", + " 'intarr': [", + " 3", + " ],", + " 'longarr': [", + " 4", + " ],", + " 'stringarr': [", + " 'foo'", + " ]", + "}" + ); + + assertConfigEquals(expectedJson, config); + } + + @Test + public void test_that_maps_are_formatted_to_json() throws IOException { + MaptypesConfig.Builder builder = new MaptypesConfig.Builder(); + builder.boolmap("foo", true); + builder.intmap("bar", 3); + builder.stringmap("hei", "hallo"); + MaptypesConfig.Innermap.Builder inner = new MaptypesConfig.Innermap.Builder(); + inner.foo(133); + builder.innermap("baz", inner); + MaptypesConfig.Nestedmap.Builder nested = new MaptypesConfig.Nestedmap.Builder(); + nested.inner("foo", 33); + builder.nestedmap("bim", nested); + + final MaptypesConfig config = new MaptypesConfig(builder); + final String expectedJson = inputJson( + "{", + " 'boolmap': {", + " 'foo': true", + " },", + " 'doublemap': {},", + " 'filemap': {},", + " 'innermap': {", + " 'baz': {", + " 'foo': 133", + " }", + " },", + " 'intmap': {", + " 'bar': 3", + " },", + " 'longmap': {},", + " 'nestedmap': {", + " 'bim': {", + " 'inner': {", + " 'foo': 33", + " }", + " }", + " },", + " 'stringmap': {", + " 'hei': 'hallo'", + " }", + "}" + ); + + assertConfigEquals(expectedJson, config); + } + + @Test + public void test_that_non_standard_types_are_formatted_as_json_strings() throws IOException { + SpecialtypesConfig.Builder builder = new SpecialtypesConfig.Builder(); + builder.myfile("thefilename"); + builder.myref("thereference"); + + final SpecialtypesConfig config = new SpecialtypesConfig(builder); + final String expectedJson = inputJson( + "{", + " 'myfile': 'thefilename',", + " 'myref': 'thereference'", + "}" + ); + + assertConfigEquals(expectedJson, config); + } + + static void assertConfigEquals(String expectedJson, ConfigInstance config) { + Slime slime = new Slime(); + ConfigInstance.serialize(config, new ConfigInstanceSerializer(slime)); + JsonFormat jsonFormat = new JsonFormat(true); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + try { + jsonFormat.encode(baos, slime); + } catch (IOException e) { + fail(); + } + + assertJsonEquals(baos.toString(), expectedJson); + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceTest.java new file mode 100644 index 00000000000..ea7312ec7ef --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceTest.java @@ -0,0 +1,119 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.foo.AppConfig; +import com.yahoo.foo.TestNonstringConfig; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + * Tests different aspects of the ConfigInstance class and its underlying Nodes. + * + * @author <a href="gv@yahoo-inc.com">G. Voldengen</a> + */ +public class ConfigInstanceTest { + private ConfigSourceSet sourceSet = new ConfigSourceSet("config-instance-test"); + + /** + * Verifies that the subscriber's configure() method is only + * called once upon subscribe, even if there are more than one + * subscribers to the same ConfigInstance. This has previously + * been a problem, since ConfigInstance.subscribe() called + * configureSubscriber(), which configures all subscribers to the + * instance. Now, the new method configureSubscriber(Subscriber) + * is called instead. + */ + @Test + public void testConfigureOnlyOnceUponSubscribe() { + final String configId = "raw:times 1\n"; + AppService service1 = new AppService(configId, sourceSet); + AppService service2 = new AppService(configId, sourceSet); + + assertEquals(1, service1.timesConfigured()); + assertEquals(1, service2.timesConfigured()); + } + + /** + * Verifies that values set in previous setConfig() calls are + * retained when the payload in a new setConfig() call does not + * overwrite them. + */ + @Test + @Ignore + public void testRetainOldValuesOnConfigUpdates() { + AppConfig config = new AppConfig(new AppConfig.Builder()); + //config.setConfig(Arrays.asList("message \"one\"", "times 333"), "", 0L); + assertEquals("one", config.message()); + assertEquals(333, config.times()); + + //config.setConfig(Arrays.asList("message \"two\""), "", 0L); + assertEquals("two", config.message()); + assertEquals("config.times retains previously set value", 333, config.times()); + + //config.setConfig(Arrays.asList("times 666"), "", 0L); + assertEquals("config.message retains previously set value", "two", config.message()); + assertEquals(666, config.times()); + } + + /** + * Verifies that an exception is thrown when one attempts to set an + * illegal config value for parameters that have default values. + */ + @Test + public void testFailUponIllegalValue() { + verifyIllegalValue("i notAnInt"); + verifyIllegalValue("i 3.0"); + + verifyIllegalValue("d noDouble"); + verifyIllegalValue("d 3.0."); + + // verifyIllegalValue("b notTrueOrFalse"); + //verifyIllegalValue("b 1"); + //verifyIllegalValue("b 0"); + + verifyIllegalValue("e undeclaredEnumValue"); + verifyIllegalValue("e 0"); + } + + private void verifyIllegalValue(String line) { + String configId = "raw:" + line + "\n"; + try { + new TestNonstring(configId); + fail("Expected ConfigurationRuntimeException when setting a parameter value of wrong type."); + } catch (RuntimeException expected) { + verifyException(expected, "Not able to create config builder for payload", "Got ConfigurationRuntimeException for the wrong reason. " + + "Expected to fail when setting a parameter value of wrong type."); + } + + } + + private void verifyException(Throwable throwable, String expected, String failMessage) { + Throwable t = throwable; + boolean ok = false; + while (t != null) { + if (t.getMessage() != null && t.getMessage().contains(expected)) { + ok = true; + break; + } + t = t.getCause(); + } + if (!ok) { + throwable.printStackTrace(); + fail(failMessage); + } + } + + private class TestNonstring { + private final ConfigSubscriber subscriber; + private final ConfigHandle<TestNonstringConfig> handle; + public TestNonstring(String configId) { + subscriber = new ConfigSubscriber(); + handle = subscriber.subscribe(TestNonstringConfig.class, configId); + subscriber.nextConfig(); + handle.getConfig(); + } + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceUtilTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceUtilTest.java new file mode 100644 index 00000000000..36ea09db1dc --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceUtilTest.java @@ -0,0 +1,172 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.config.ConfigBuilder; +import com.yahoo.config.ConfigurationRuntimeException; +import com.yahoo.config.FileReference; +import com.yahoo.foo.FunctionTestConfig; +import com.yahoo.foo.TestNodefsConfig; +import com.yahoo.vespa.config.ConfigPayload; +import com.yahoo.vespa.config.ConfigPayloadBuilder; +import org.junit.Test; + +import java.io.File; +import java.util.Arrays; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.containsString; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static com.yahoo.foo.FunctionTestConfig.*; + +/** + * @author lulf + * @since 5.1 + */ +public class ConfigInstanceUtilTest { + + @Test + public void require_that_builder_values_can_be_overridden_by_another_builder() { + FunctionTestConfig.Builder destination = createVariableAccessBuilder(); + + FunctionTestConfig.Builder source = new FunctionTestConfig.Builder() + .int_val(-1) + .intarr(0) + .doublearr(0.0) + .basicStruct(new FunctionTestConfig.BasicStruct.Builder() + .bar(-1) + .intArr(0)) + .myarray(new FunctionTestConfig.Myarray.Builder() + .intval(-1) + .refval("") + .fileVal("") + .myStruct(new FunctionTestConfig.Myarray.MyStruct.Builder() + .a(0) + )); + + ConfigInstanceUtil.setValues(destination, source); + + FunctionTestConfig result = new FunctionTestConfig(destination); + assertThat(result.int_val(), is(-1)); + assertThat(result.string_val(), is("foo")); + assertThat(result.intarr().size(), is(1)); + assertThat(result.intarr(0), is(0)); + assertThat(result.longarr().size(), is(2)); + assertThat(result.doublearr().size(), is(3)); + assertEquals(2344.0, result.doublearr(0), 0.01); + assertEquals(123.0, result.doublearr(1), 0.01); + assertEquals(0.0, result.doublearr(2), 0.01); + assertThat(result.basicStruct().bar(), is(-1)); + assertThat(result.basicStruct().foo(), is("basicFoo")); + assertThat(result.basicStruct().intArr().size(), is(3)); + assertThat(result.basicStruct().intArr(0), is(310)); + assertThat(result.basicStruct().intArr(1), is(311)); + assertThat(result.basicStruct().intArr(2), is(0)); + assertThat(result.myarray().size(), is(3)); + assertThat(result.myarray(2).intval(), is(-1)); + assertThat(result.myarray(2).refval(), is("")); + assertThat(result.myarray(2).fileVal().value(), is("")); + assertThat(result.myarray(2).myStruct().a(), is(0)); + + } + + @Test(expected = ConfigurationRuntimeException.class) + public void require_that_invalid_builders_fail() { + ConfigInstanceUtil.setValues(new FakeBuilder(), new FakeBuilder()); + } + + private static class FakeBuilder implements ConfigBuilder { + } + + static FunctionTestConfig.Builder createVariableAccessBuilder() { + return new FunctionTestConfig.Builder(). + bool_val(false). + bool_with_def(true). + int_val(5). + int_with_def(-14). + long_val(12345678901L). + long_with_def(-9876543210L). + double_val(41.23). + double_with_def(-12). + string_val("foo"). + stringwithdef("bar and foo"). + enum_val(Enum_val.FOOBAR). + enumwithdef(Enumwithdef.BAR2). + refval(":parent:"). + refwithdef(":parent:"). + fileVal("etc"). + pathVal(FileReference.mockFileReferenceForUnitTesting(new File("src/test/resources/configs/def-files/function-test.def"))). + boolarr(false). + longarr(9223372036854775807L). + longarr(-9223372036854775808L). + doublearr(2344.0). + doublearr(123.0). + stringarr("bar"). + enumarr(Enumarr.VALUES). + refarr(Arrays.asList(":parent:", ":parent", "parent:")). // test collection based setter + fileArr("bin"). + + basicStruct(new FunctionTestConfig.BasicStruct.Builder(). + foo("basicFoo"). + bar(3). + intArr(310).intArr(311)). + + rootStruct(new FunctionTestConfig.RootStruct.Builder(). + inner0(new FunctionTestConfig.RootStruct.Inner0.Builder(). + index(11)). + inner1(new FunctionTestConfig.RootStruct.Inner1.Builder(). + index(12)). + innerArr(new FunctionTestConfig.RootStruct.InnerArr.Builder(). + boolVal(true). + stringVal("deep")). + innerArr(new FunctionTestConfig.RootStruct.InnerArr.Builder(). + boolVal(false). + stringVal("blue a=\"escaped\""))). + + myarray(new FunctionTestConfig.Myarray.Builder(). + intval(-5). + stringval("baah"). + stringval("yikes"). + enumval(Myarray.Enumval.INNER). + refval(":parent:"). + fileVal("file0"). + anotherarray(new FunctionTestConfig.Myarray.Anotherarray.Builder(). + foo(7)). + myStruct(new FunctionTestConfig.Myarray.MyStruct.Builder(). + a(1). + b(2))). + + myarray(new FunctionTestConfig.Myarray.Builder(). + intval(5). + enumval(Myarray.Enumval.INNER). + refval(":parent:"). + fileVal("file1"). + anotherarray(new FunctionTestConfig.Myarray.Anotherarray.Builder(). + foo(1). + foo(2)). + myStruct(new FunctionTestConfig.Myarray.MyStruct.Builder(). + a(-1). + b(-2))); + + } + + @Test + public void test_require_private_getter() { + FunctionTestConfig.Builder builder = createVariableAccessBuilder(); + assertEquals(ConfigInstanceUtil.getField(builder, "int_val"), 5); + FunctionTestConfig conf = new FunctionTestConfig(builder); + assertEquals(conf.int_val(), 5); + } + + @Test + public void testGetNewInstanceErrorMessage() { + ConfigPayloadBuilder payloadBuilder = new ConfigPayloadBuilder(); + try { + ConfigInstanceUtil.getNewInstance(TestNodefsConfig.class, "id0", ConfigPayload.fromBuilder(payloadBuilder)); + assert(false); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString("Failed creating new instance of 'com.yahoo.foo.TestNodefsConfig' for config id 'id0':")); + } + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigInterruptedExceptionTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigInterruptedExceptionTest.java new file mode 100644 index 00000000000..d760a1c1b72 --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigInterruptedExceptionTest.java @@ -0,0 +1,19 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author lulf + * @since 5.1 + */ +public class ConfigInterruptedExceptionTest { + @Test + public void require_that_throwable_is_preserved() { + ConfigInterruptedException e = new ConfigInterruptedException(new RuntimeException("foo")); + assertThat(e.getCause().getMessage(), is("foo")); + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigSetSubscriptionTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigSetSubscriptionTest.java new file mode 100644 index 00000000000..adbc11b7ca0 --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigSetSubscriptionTest.java @@ -0,0 +1,133 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import static org.junit.Assert.*; + +import com.yahoo.foo.AppConfig; +import com.yahoo.foo.SimpletypesConfig; +import com.yahoo.foo.StringConfig; +import com.yahoo.config.subscription.impl.ConfigSubscription; +import com.yahoo.vespa.config.ConfigKey; +import com.yahoo.vespa.config.TimingValues; +import org.junit.Test; + +public class ConfigSetSubscriptionTest { + + @Test + public void testConfigSubscription() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + ConfigSet configSet = new ConfigSet(); + AppConfig.Builder a0builder = new AppConfig.Builder().message("A message, 0").times(88); + configSet.addBuilder("app/0", a0builder); + AppConfig.Builder a1builder = new AppConfig.Builder().message("A message, 1").times(89); + configSet.addBuilder("app/1", a1builder); + + ConfigSubscription<AppConfig> c1 = ConfigSubscription.get( + new ConfigKey<>(AppConfig.class, "app/0"), + subscriber, + configSet, + new TimingValues()); + ConfigSubscription<AppConfig> c2 = ConfigSubscription.get( + new ConfigKey<>(AppConfig.class, "app/1"), + subscriber, + configSet, + new TimingValues()); + + assertTrue(c1.equals(c1)); + assertFalse(c1.equals(c2)); + } + + @Test(expected = IllegalArgumentException.class) + public void testUnknownKey() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + ConfigSet configSet = new ConfigSet(); + AppConfig.Builder a0builder = new AppConfig.Builder().message("A message, 0").times(88); + configSet.addBuilder("app/0", a0builder); + + ConfigSubscription.get( + new ConfigKey<>(SimpletypesConfig.class, "simpletypes/1"), + subscriber, + configSet, + new TimingValues()); + } + + @Test + public void testConfigSetBasic() { + ConfigSet myConfigs = new ConfigSet(); + AppConfig.Builder a0builder = new AppConfig.Builder().message("A message, 0").times(88); + AppConfig.Builder a1builder = new AppConfig.Builder().message("A message, 1").times(89); + StringConfig.Builder barBuilder = new StringConfig.Builder().stringVal("StringVal"); + myConfigs.addBuilder("app/0", a0builder); + myConfigs.addBuilder("app/1", a1builder); + myConfigs.addBuilder("bar", barBuilder); + ConfigSubscriber subscriber = new ConfigSubscriber(myConfigs); + ConfigHandle<AppConfig> hA0 = subscriber.subscribe(AppConfig.class, "app/0"); + ConfigHandle<AppConfig> hA1 = subscriber.subscribe(AppConfig.class, "app/1"); + ConfigHandle<StringConfig> hS = subscriber.subscribe(StringConfig.class, "bar"); + + assertTrue(subscriber.nextConfig(0)); + assertTrue(hA0.isChanged()); + assertTrue(hA1.isChanged()); + assertTrue(hS.isChanged()); + + assertEquals(hA0.getConfig().message(), "A message, 0"); + assertEquals(hA1.getConfig().message(), "A message, 1"); + assertEquals(hA0.getConfig().times(), 88); + assertEquals(hA1.getConfig().times(), 89); + + assertFalse(subscriber.nextConfig(10)); + assertFalse(hA0.isChanged()); + assertFalse(hA1.isChanged()); + assertFalse(hS.isChanged()); + assertEquals(hA0.getConfig().message(), "A message, 0"); + assertEquals(hA1.getConfig().message(), "A message, 1"); + assertEquals(hA0.getConfig().times(), 88); + assertEquals(hA1.getConfig().times(), 89); + assertEquals(hS.getConfig().stringVal(), "StringVal"); + + //Reconfigure all configs, generation should change + a0builder.message("A new message, 0").times(880); + a1builder.message("A new message, 1").times(890); + barBuilder.stringVal("new StringVal"); + subscriber.reload(1); + assertTrue(subscriber.nextConfig(0)); + assertTrue(hA0.isChanged()); + assertTrue(hA1.isChanged()); + assertTrue(hS.isChanged()); + + assertEquals(hA0.getConfig().message(), "A new message, 0"); + assertEquals(hA1.getConfig().message(), "A new message, 1"); + assertEquals(hA0.getConfig().times(), 880); + assertEquals(hA1.getConfig().times(), 890); + assertEquals(hS.getConfig().stringVal(), "new StringVal"); + + // Reconfigure only one + a0builder.message("Another new message, 0").times(8800); + subscriber.reload(2); + assertTrue(subscriber.nextConfig(0)); + assertTrue(hA0.isChanged()); + assertFalse(hA1.isChanged()); + assertFalse(hS.isChanged()); + + assertEquals(hA0.getConfig().message(), "Another new message, 0"); + assertEquals(hA1.getConfig().message(), "A new message, 1"); + assertEquals(hA0.getConfig().times(), 8800); + assertEquals(hA1.getConfig().times(), 890); + assertEquals(hS.getConfig().stringVal(), "new StringVal"); + + //Reconfigure only one, and only one field on the builder + a1builder.message("Yet another message, 1"); + subscriber.reload(3); + assertTrue(subscriber.nextConfig(0)); + assertFalse(hA0.isChanged()); + assertTrue(hA1.isChanged()); + assertFalse(hS.isChanged()); + + assertEquals(hA0.getConfig().message(), "Another new message, 0"); + assertEquals(hA1.getConfig().message(), "Yet another message, 1"); + assertEquals(hA0.getConfig().times(), 8800); + assertEquals(hA1.getConfig().times(), 890); + assertEquals(hS.getConfig().stringVal(), "new StringVal"); + } + +} diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigSetTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigSetTest.java new file mode 100644 index 00000000000..668fa68d264 --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigSetTest.java @@ -0,0 +1,24 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.foo.SimpletypesConfig; +import org.junit.Test; + +import java.util.regex.Pattern; + +import static org.junit.Assert.assertTrue; + +/** + * @author lulf + * @since 5.1 + */ +public class ConfigSetTest { + @Test + public void testToString() { + ConfigSet set = new ConfigSet(); + SimpletypesConfig.Builder builder = new SimpletypesConfig.Builder(); + set.addBuilder("foo", builder); + assertTrue(Pattern.matches("name=simpletypes,namespace=foo,configId=foo=>com.yahoo.foo.SimpletypesConfig.*", + set.toString())); + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigSourceSetTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigSourceSetTest.java new file mode 100755 index 00000000000..23633a62cca --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigSourceSetTest.java @@ -0,0 +1,68 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import org.junit.Test; +import org.junit.After; + +import java.util.Set; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + +/** + * @author <a href="gv@yahoo-inc.com">G. Voldengen</a> + */ +public class ConfigSourceSetTest { + @Test + public void testEquals() { + assertTrue(new ConfigSourceSet().equals(new ConfigSourceSet())); + assertFalse(new ConfigSourceSet().equals(new ConfigSourceSet(new String[]{"a"}))); + + assertTrue(new ConfigSourceSet(new String[]{"a"}).equals(new ConfigSourceSet(new String[]{"a"}))); + assertTrue(new ConfigSourceSet(new String[]{"a"}).equals(new ConfigSourceSet(new String[]{" A "}))); + assertTrue(new ConfigSourceSet(new String[]{"a"}).equals(new ConfigSourceSet(new String[]{"A", "a"}))); + assertTrue(new ConfigSourceSet(new String[]{"A"}).equals(new ConfigSourceSet(new String[]{"a", " a "}))); + + assertFalse(new ConfigSourceSet(new String[]{"a"}).equals(new ConfigSourceSet(new String[]{"b"}))); + assertFalse(new ConfigSourceSet(new String[]{"a"}).equals(new ConfigSourceSet(new String[]{"a", "b"}))); + + assertTrue(new ConfigSourceSet(new String[]{"a", "b"}).equals(new ConfigSourceSet(new String[]{"a", "b"}))); + assertTrue(new ConfigSourceSet(new String[]{"b", "a"}).equals(new ConfigSourceSet(new String[]{"a", "b"}))); + assertTrue(new ConfigSourceSet(new String[]{"A", " b"}).equals(new ConfigSourceSet(new String[]{"a ", "B"}))); + assertTrue(new ConfigSourceSet(new String[]{"b", "a", "c"}) + .equals(new ConfigSourceSet(new String[]{"a", "b", "c"}))); + + assertFalse(new ConfigSourceSet(new String[]{"a", "b"}).equals(new ConfigSourceSet(new String[]{"b", "c"}))); + assertFalse(new ConfigSourceSet().equals("foo")); + } + + @Test + public void testIterationOrder() { + String[] hosts = new String[]{"primary", "fallback", "last-resort"}; + ConfigSourceSet css = new ConfigSourceSet(hosts); + + Set<String> sources = css.getSources(); + assertEquals(hosts.length, sources.size()); + int i = 0; + for (String s : sources) { + assertEquals(hosts[i++], s); + } + } + + @Test + public void testDefaultSourceFromProperty() { + // TODO: Unable to set environment, so only able to test property usage for now + System.setProperty("configsources", "foo:123,bar:345,tcp/baz:333,quux"); + ConfigSourceSet set = ConfigSourceSet.createDefault(); + assertThat(set.getSources().size(), is(4)); + assertTrue(set.getSources().contains("tcp/foo:123")); + assertTrue(set.getSources().contains("tcp/bar:345")); + assertTrue(set.getSources().contains("tcp/baz:333")); + assertTrue(set.getSources().contains("tcp/quux")); + } + + @After + public void cleanup() { + System.clearProperty("configsources"); + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigSourceTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigSourceTest.java new file mode 100644 index 00000000000..d928592ebe9 --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigSourceTest.java @@ -0,0 +1,45 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.foo.SimpletypesConfig; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; + +import static junit.framework.TestCase.fail; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author lulf + * @since 5.1 + */ +public class ConfigSourceTest { + @Test(expected = IllegalArgumentException.class) + public void require_that_FileSource_throws_exception_on_invalid_file() { + new FileSource(new File("invalid")); + } + + @Test(expected = IllegalArgumentException.class) + public void require_that_DirSource_throws_exception_on_invalid_dir() { + new DirSource(new File("invalid")); + } + + @Rule + public TemporaryFolder tmpDir = new TemporaryFolder(); + + @Test + public void require_that_DirSource_throws_exception_on_missing_file() throws IOException { + File folder = tmpDir.newFolder(); + DirSource dirSource = new DirSource(folder); + try { + ConfigGetter.getConfig(SimpletypesConfig.class, "dir:" + tmpDir, dirSource); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), is("Could not find a config file for '" + SimpletypesConfig.getDefName() + "' in '" + folder + "/'")); + } + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigSubscriptionTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigSubscriptionTest.java new file mode 100644 index 00000000000..9fa299aef83 --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigSubscriptionTest.java @@ -0,0 +1,100 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.config.ConfigInstance; +import com.yahoo.config.ConfigurationRuntimeException; +import com.yahoo.foo.SimpletypesConfig; +import com.yahoo.foo.AppConfig; +import com.yahoo.config.subscription.impl.ConfigSubscription; +import com.yahoo.vespa.config.ConfigKey; +import com.yahoo.vespa.config.TimingValues; + +import org.junit.Ignore; +import org.junit.Test; + +import java.util.Collections; +import java.util.List; + +import static junit.framework.TestCase.fail; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author musum, lulf + * @since 5.1 + */ +public class ConfigSubscriptionTest { + @Test + public void testEquals() { + ConfigSubscriber sub = new ConfigSubscriber(); + final String payload = "boolval true"; + ConfigSubscription<SimpletypesConfig> a = ConfigSubscription.get(new ConfigKey<>(SimpletypesConfig.class, "test"), + sub, new RawSource(payload), new TimingValues()); + ConfigSubscription<SimpletypesConfig> b = ConfigSubscription.get(new ConfigKey<>(SimpletypesConfig.class, "test"), + sub, new RawSource(payload), new TimingValues()); + ConfigSubscription<SimpletypesConfig> c = ConfigSubscription.get(new ConfigKey<>(SimpletypesConfig.class, "test2"), + sub, new RawSource(payload), new TimingValues()); + assertThat(a, is(b)); + assertThat(a, is(a)); + assertThat(b, is(b)); + assertThat(c, is(c)); + assertThat(a, is(not(c))); + assertThat(b, is(not(c))); + + ConfigSubscriber subscriber = new ConfigSubscriber(); + ConfigSet configSet = new ConfigSet(); + AppConfig.Builder a0builder = new AppConfig.Builder().message("A message, 0").times(88); + configSet.addBuilder("app/0", a0builder); + AppConfig.Builder a1builder = new AppConfig.Builder().message("A message, 1").times(89); + configSet.addBuilder("app/1", a1builder); + + ConfigSubscription<AppConfig> c1 = ConfigSubscription.get( + new ConfigKey<>(AppConfig.class, "app/0"), + subscriber, + configSet, + new TimingValues()); + ConfigSubscription<AppConfig> c2 = ConfigSubscription.get( + new ConfigKey<>(AppConfig.class, "app/1"), + subscriber, + configSet, + new TimingValues()); + + assertTrue(c1.equals(c1)); + assertFalse(c1.equals(c2)); + } + + @Test + public void testSubscribeInterface() { + ConfigSubscriber sub = new ConfigSubscriber(); + ConfigHandle<SimpletypesConfig> handle = sub.subscribe(SimpletypesConfig.class, "raw:boolval true", 10000); + assertNotNull(handle); + sub.nextConfig(); + assertThat(handle.getConfig().boolval(), is(true)); + //assertTrue(sub.getSource() instanceof RawSource); + } + + // Test that subscription is closed and subscriptionHandles is empty if we get an exception (only the last is possible to + // test right now). + @Test + @Ignore + public void testSubscribeWithException() { + TestConfigSubscriber sub = new TestConfigSubscriber(); + ConfigSourceSet configSourceSet = new ConfigSourceSet(Collections.singletonList("tcp/localhost:99999")); + try { + sub.subscribe(SimpletypesConfig.class, "configid", configSourceSet, new TimingValues().setSubscribeTimeout(100)); + fail(); + } catch (ConfigurationRuntimeException e) { + assertThat(sub.getSubscriptionHandles().size(), is(0)); + } + } + + private static class TestConfigSubscriber extends ConfigSubscriber { + List<ConfigHandle<? extends ConfigInstance>> getSubscriptionHandles() { + return subscriptionHandles; + } + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigURITest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigURITest.java new file mode 100644 index 00000000000..15cfff0448a --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/ConfigURITest.java @@ -0,0 +1,46 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author lulf + * @since 5.1 + */ +public class ConfigURITest { + @Test + public void testDefaultUri() { + ConfigURI uri = ConfigURI.createFromId("foo"); + assertThat(uri.getConfigId(), is("foo")); + assertTrue(uri.getSource() instanceof ConfigSourceSet); + } + + @Test + public void testFileUri() throws IOException { + File file = File.createTempFile("foo", ".cfg"); + ConfigURI uri = ConfigURI.createFromId("file:" + file.getAbsolutePath()); + assertThat(uri.getConfigId(), is("")); + assertTrue(uri.getSource() instanceof FileSource); + } + + @Test + public void testDirUri() throws IOException { + ConfigURI uri = ConfigURI.createFromId("dir:."); + assertThat(uri.getConfigId(), is("")); + assertTrue(uri.getSource() instanceof DirSource); + } + + @Test + public void testCustomUri() { + ConfigURI uri = ConfigURI.createFromIdAndSource("foo", new ConfigSet()); + assertThat(uri.getConfigId(), is("foo")); + assertTrue(uri.getSource() instanceof ConfigSet); + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/DefaultConfigTest.java b/config/src/test/java/com/yahoo/config/subscription/DefaultConfigTest.java new file mode 100644 index 00000000000..55b1eb97bee --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/DefaultConfigTest.java @@ -0,0 +1,66 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.foo.*; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Tests reading of a config containing + * <ul> + * <li>Missing values + * <li>Default values + * </ul> + * <p/> + * for + * <p/> + * <ul> + * <li>String and + * <li>Reference + * </ul> + * + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + */ +public class DefaultConfigTest { + static final String CONFIG_ID = "raw:" + + "nondefaultstring ####-------missing--------\n" + + "defaultstring \"thedefault\"\n" + + "nondefaultreference ####-------missing--------\n" + + "defaultreference \"thedefault\"\n"; + + @Test(expected = IllegalArgumentException.class) + public void testFailUponUnitializedValue() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + subscriber.subscribe(DefaulttestConfig.class, "raw:" + + "defaultstring \"new value\""); + subscriber.nextConfig(); + subscriber.close(); + } + + /** + * Reads a config from a string which is exactly like one returned from + * the config server given only default values for this config. + * The parsing code is the same whether the reading happens from string + * or from a server connection, so this tests that this config can be + * received correctly from the server + */ + @Test + public void testDefaultConfig() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + ConfigHandle<DefaulttestConfig> h = subscriber.subscribe(DefaulttestConfig.class, CONFIG_ID); + assertTrue(subscriber.nextConfig()); + DefaulttestConfig config = h.getConfig(); + verifyConfigValues(config); + subscriber.close(); + } + + private static void verifyConfigValues(DefaulttestConfig config) { + assertEquals("####-------missing--------", config.nondefaultstring()); + assertEquals("thedefault", config.defaultstring()); + assertEquals("####-------missing--------", config.nondefaultreference()); + assertEquals("thedefault", config.defaultreference()); + } + +} diff --git a/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java b/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java new file mode 100644 index 00000000000..8936c62784b --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/FunctionTest.java @@ -0,0 +1,259 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.foo.FunctionTestConfig; + +import org.junit.Before; +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +/** + * Test most data types and features of the config client API, + * e.g. parameter access, missing default values, and more. + * <p/> + * NOTE: this test does NOT test null default values. + * + * @author gjoranv + */ +public class FunctionTest { + + public static final String PATH = "src/test/resources/configs/function-test/"; + + private FunctionTestConfig config; + private ConfigSourceSet sourceSet = new ConfigSourceSet("function-test"); + + public void configure(FunctionTestConfig config, ConfigSourceSet sourceSet) { + this.config = config; + assertEquals(this.sourceSet, sourceSet); + } + + @Before + public void resetConfig() { + config = null; + } + + @Test + public void testVariableAccess() { + assertNull(config); + String configId = "file:" + PATH + "variableaccess.txt"; + ConfigGetter<FunctionTestConfig> getter = new ConfigGetter<>(FunctionTestConfig.class); + assertVariableAccessValues(getter.getConfig(configId), configId); + } + + @Test + public void testDefaultValues() { + assertNull(config); + String configId = "file:" + PATH + "defaultvalues.txt"; + ConfigGetter<FunctionTestConfig> getter = new ConfigGetter<>(FunctionTestConfig.class); + FunctionTestConfig config = getter.getConfig(configId); + + assertFalse(config.bool_val()); + assertFalse(config.bool_with_def()); + assertEquals(5, config.int_val()); + assertEquals(-545, config.int_with_def()); + assertEquals(1234567890123L, config.long_val()); + assertEquals(-50000000000L, config.long_with_def()); + assertEquals(41.23, config.double_val(), 0.000001); + assertEquals(-6.43, config.double_with_def(), 0.000001); + assertEquals("foo", config.string_val()); + assertEquals("foobar", config.stringwithdef()); + assertEquals(FunctionTestConfig.Enum_val.FOOBAR, config.enum_val()); + assertEquals(FunctionTestConfig.Enumwithdef.BAR2, config.enumwithdef()); + assertEquals(configId, config.refval()); + assertEquals(configId, config.refwithdef()); + assertEquals("vespa.log", config.fileVal().value()); + assertEquals(1, config.boolarr().size()); + assertEquals(0, config.intarr().size()); + assertEquals(0, config.longarr().size()); + assertEquals(2, config.doublearr().size()); + assertEquals(1, config.stringarr().size()); + assertEquals(1, config.enumarr().size()); + assertEquals(0, config.refarr().size()); + assertEquals(0, config.fileArr().size()); + + assertEquals(3, config.basicStruct().bar()); + assertEquals(1, config.basicStruct().intArr().size()); + assertEquals(10, config.basicStruct().intArr(0)); + assertEquals(11, config.rootStruct().inner0().index()); + assertEquals(12, config.rootStruct().inner1().index()); + + assertEquals(2, config.myarray().size()); + assertEquals(1, config.myarray(0).myStruct().a()); + assertEquals(-1, config.myarray(1).myStruct().a()); + assertEquals("command.com", config.myarray(0).fileVal().value()); + assertEquals("display.sys", config.myarray(1).fileVal().value()); + } + + + @Test + public void testRandomOrder() { + assertNull(config); + String configId = "file:" + PATH + "randomorder.txt"; + ConfigGetter<FunctionTestConfig> getter = new ConfigGetter<>(FunctionTestConfig.class); + FunctionTestConfig config = getter.getConfig(configId); + assertFalse(config.bool_val()); + assertTrue(config.bool_with_def()); + assertEquals(5, config.int_val()); + assertEquals(-14, config.int_with_def()); + assertEquals(666000666000L, config.long_val()); + assertEquals(-333000333000L, config.long_with_def()); + assertEquals(41.23, config.double_val(), 0.000001); + assertEquals(-12, config.double_with_def(), 0.000001); + assertEquals("foo", config.string_val()); + assertEquals("bar and foo", config.stringwithdef()); + assertEquals(FunctionTestConfig.Enum_val.FOOBAR, config.enum_val()); + assertEquals(FunctionTestConfig.Enumwithdef.BAR2, config.enumwithdef()); + assertEquals(configId, config.refval()); + assertEquals(configId, config.refwithdef()); + assertEquals("autoexec.bat", config.fileVal().value()); + assertEquals(1, config.boolarr().size()); + assertEquals(0, config.intarr().size()); + assertEquals(0, config.longarr().size()); + assertEquals(2, config.doublearr().size()); + assertEquals(1, config.stringarr().size()); + assertEquals(1, config.enumarr().size()); + assertEquals(0, config.refarr().size()); + assertEquals(0, config.fileArr().size()); + assertEquals(2, config.myarray().size()); + } + + @Test + public void testLackingDefaults() throws IOException { + attemptLacking("bool_val", false); + attemptLacking("int_val", false); + attemptLacking("long_val", false); + attemptLacking("double_val", false); + attemptLacking("string_val", false); + attemptLacking("enum_val", false); + attemptLacking("refval", false); + attemptLacking("fileVal", false); + + attemptLacking("boolarr", true); + attemptLacking("intarr", true); + attemptLacking("longarr", true); + attemptLacking("doublearr", true); + attemptLacking("enumarr", true); + attemptLacking("stringarr", true); + attemptLacking("refarr", true); + attemptLacking("fileArr", true); + attemptLacking("myarray", true); + + attemptLacking("basicStruct.bar", false); + attemptLacking("rootStruct.inner0.index", false); + attemptLacking("rootStruct.inner1.index", false); +// attemptLacking("rootStruct.innerArr[0].stringVal", false); + + attemptLacking("myarray[0].stringval", true); + attemptLacking("myarray[0].refval", false); + attemptLacking("myarray[0].anotherarray", true); + attemptLacking("myarray[0].anotherarray", true); + attemptLacking("myarray[0].myStruct.a", false); + } + + private void attemptLacking(String param, boolean isArray) throws IOException { + BufferedReader in = new BufferedReader(new FileReader(new File(PATH + "defaultvalues.txt"))); + StringBuilder config = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) { + if ((line.length() > param.length()) && + param.equals(line.substring(0, param.length())) && + (line.charAt(param.length()) == ' ' || line.charAt(param.length()) == '[')) { + // Ignore values matched + } else { + config.append(line).append("\n"); + } + } + //System.out.println("Config lacking " + param + "-> " + config + "\n"); + try { + ConfigGetter<FunctionTestConfig> getter = new ConfigGetter<FunctionTestConfig>(FunctionTestConfig.class); + getter.getConfig("raw:\n" + config); + if (isArray) { + // Arrays are empty by default + return; + } + fail("Expected to fail when not specifying value " + param + " without default"); + } catch (IllegalArgumentException expected) { + if (isArray) { + fail("Arrays should be empty by default."); + } + } + } + + public static void assertVariableAccessValues(FunctionTestConfig config, String configId) { + assertTrue(config.bool_with_def()); + assertEquals(5, config.int_val()); + assertEquals(-14, config.int_with_def()); + assertEquals(12345678901L, config.long_val()); + assertEquals(-9876543210L, config.long_with_def()); + assertEquals(41.23, config.double_val(), 0.000001); + assertEquals(-12, config.double_with_def(), 0.000001); + assertEquals("foo", config.string_val()); + assertEquals("bar and foo", config.stringwithdef()); + assertEquals(FunctionTestConfig.Enum_val.FOOBAR, config.enum_val()); + assertEquals(FunctionTestConfig.Enumwithdef.BAR2, config.enumwithdef()); + assertEquals(configId, config.refval()); + assertEquals(configId, config.refwithdef()); + assertEquals("etc", config.fileVal().value()); + assertEquals(1, config.boolarr().size()); + assertEquals(1, config.boolarr().size()); // new api with accessor for a List of the original Java type + assertEquals(false, config.boolarr().get(0)); // new List api + assertEquals(false, config.boolarr(0)); // short-hand + assertEquals(0, config.intarr().size()); + assertEquals(2, config.longarr().size()); + assertEquals(Long.MAX_VALUE, config.longarr(0)); + assertThat(config.longarr().get(1), is(Long.MIN_VALUE)); + assertEquals(2, config.doublearr().size()); + assertEquals(1, config.stringarr().size()); + assertEquals(1, config.enumarr().size()); + assertEquals(FunctionTestConfig.Enumarr.VALUES, config.enumarr().get(0)); // new List api, don't have to call value() + assertEquals(3, config.refarr().size()); + assertEquals(1, config.fileArr().size()); + assertEquals(configId, config.refarr(0)); + assertEquals(":parent", config.refarr(1)); + assertEquals("parent:", config.refarr(2)); + assertEquals("bin", config.fileArr(0).value()); + assertEquals("pom.xml", config.pathArr(0).toString()); + assertEquals("pom.xml", config.pathMap("one").toString()); + + assertEquals("basicFoo", config.basicStruct().foo()); + assertEquals(3, config.basicStruct().bar()); // new List api + assertEquals(2, config.basicStruct().intArr().size()); + assertThat(config.basicStruct().intArr().get(0), is(310)); // new List api + assertThat(config.basicStruct().intArr().get(1), is(311)); // new List api + assertEquals(310, config.basicStruct().intArr(0)); // short-hand + assertEquals("inner0", config.rootStruct().inner0().name()); // new List api + assertEquals(11, config.rootStruct().inner0().index()); + assertEquals("inner1", config.rootStruct().inner1().name()); + assertEquals(12, config.rootStruct().inner1().index()); + assertEquals(2, config.rootStruct().innerArr().size()); + assertEquals(true, config.rootStruct().innerArr(0).boolVal()); + assertEquals("deep", config.rootStruct().innerArr(0).stringVal()); + assertEquals(false, config.rootStruct().innerArr(1).boolVal()); + assertEquals("blue a=\"escaped\"", config.rootStruct().innerArr(1).stringVal()); + + assertEquals(2, config.myarray().size()); // new List api + assertEquals(configId, config.myarray().get(0).refval()); // new List api + assertEquals(configId, config.myarray(0).refval()); // short-hand + assertEquals("file0", config.myarray(0).fileVal().value()); + assertEquals(1, config.myarray(0).myStruct().a()); + assertEquals(2, config.myarray(0).myStruct().b()); + assertEquals(configId, config.myarray(1).refval()); + assertEquals("file1", config.myarray(1).fileVal().value()); + assertEquals(-1, config.myarray(1).myStruct().a()); + assertEquals(-2, config.myarray(1).myStruct().b()); + } + +} diff --git a/config/src/test/java/com/yahoo/config/subscription/GenericConfigSubscriberTest.java b/config/src/test/java/com/yahoo/config/subscription/GenericConfigSubscriberTest.java new file mode 100644 index 00000000000..26b47f7621c --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/GenericConfigSubscriberTest.java @@ -0,0 +1,75 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import java.util.*; + +import com.yahoo.config.subscription.impl.GenericConfigHandle; +import com.yahoo.config.subscription.impl.GenericConfigSubscriber; +import com.yahoo.config.subscription.impl.JRTConfigRequester; +import com.yahoo.config.subscription.impl.JRTConfigRequesterTest; +import com.yahoo.config.subscription.impl.MockConnection; +import com.yahoo.vespa.config.ConfigKey; +import com.yahoo.vespa.config.JRTConnectionPool; +import com.yahoo.vespa.config.protocol.CompressionType; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + +/** + * + * Test cases for the "generic" (class-less) subscription mechanism. + * + * @author lulf + * @since 5.1 + */ +public class GenericConfigSubscriberTest { + + @Test + public void testSubscribeGeneric() { + Map<ConfigSourceSet, JRTConfigRequester> requesters = new HashMap<>(); + ConfigSourceSet sourceSet = new ConfigSourceSet("blabla"); + requesters.put(sourceSet, JRTConfigRequester.get(new MockConnection(), JRTConfigRequesterTest.getTestTimingValues())); + GenericConfigSubscriber sub = new GenericConfigSubscriber(requesters); + final List<String> defContent = Arrays.asList("myVal int"); + GenericConfigHandle handle = sub.subscribe(new ConfigKey<>("simpletypes", "id", "config"), defContent, sourceSet, JRTConfigRequesterTest.getTestTimingValues()); + assertTrue(sub.nextConfig()); + assertTrue(handle.isChanged()); + assertThat(handle.getRawConfig().getPayload().withCompression(CompressionType.UNCOMPRESSED).toString(), is("{}")); // MockConnection returns empty string + assertFalse(sub.nextConfig()); + assertFalse(handle.isChanged()); + } + + @Test + public void testGenericRequesterPooling() { + ConfigSourceSet source1 = new ConfigSourceSet("tcp/foo:78"); + ConfigSourceSet source2 = new ConfigSourceSet("tcp/bar:79"); + JRTConfigRequester req1 = JRTConfigRequester.get(new JRTConnectionPool(source1), JRTConfigRequesterTest.getTestTimingValues()); + JRTConfigRequester req2 = JRTConfigRequester.get(new JRTConnectionPool(source2), JRTConfigRequesterTest.getTestTimingValues()); + Map<ConfigSourceSet, JRTConfigRequester> requesters = new LinkedHashMap<>(); + requesters.put(source1, req1); + requesters.put(source2, req2); + GenericConfigSubscriber sub = new GenericConfigSubscriber(requesters); + assertEquals(sub.requesters().get(source1).getConnectionPool().getCurrent().getAddress(), "tcp/foo:78"); + assertEquals(sub.requesters().get(source2).getConnectionPool().getCurrent().getAddress(), "tcp/bar:79"); + + } + + @Test(expected=UnsupportedOperationException.class) + public void testOverriddenSubscribeInvalid1() { + GenericConfigSubscriber sub = new GenericConfigSubscriber(); + sub.subscribe(null, null); + } + + @Test(expected=UnsupportedOperationException.class) + public void testOverriddenSubscribeInvalid2() { + GenericConfigSubscriber sub = new GenericConfigSubscriber(); + sub.subscribe(null, null, 0l); + } + + @Test(expected=UnsupportedOperationException.class) + public void testOverriddenSubscribeInvalid3() { + GenericConfigSubscriber sub = new GenericConfigSubscriber(); + sub.subscribe(null, null, ""); + } +}
\ No newline at end of file diff --git a/config/src/test/java/com/yahoo/config/subscription/NamespaceTest.java b/config/src/test/java/com/yahoo/config/subscription/NamespaceTest.java new file mode 100644 index 00000000000..90be475bb94 --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/NamespaceTest.java @@ -0,0 +1,19 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.myproject.config.NamespaceConfig; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author gjoranv + */ +public class NamespaceTest { + @Test + public void verifyConfigClassWithExplicitNamespace() { + NamespaceConfig config = new ConfigGetter<>(NamespaceConfig.class).getConfig("raw: a 0\n"); + assertThat(config.a(), is(0)); + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/UnicodeTest.java b/config/src/test/java/com/yahoo/config/subscription/UnicodeTest.java new file mode 100644 index 00000000000..355c94e03b6 --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/UnicodeTest.java @@ -0,0 +1,31 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription; + +import com.yahoo.foo.UnicodeConfig; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Tests reading of a config containing unicode characters in UTF-8 + * + * @author Vidar Larsen + * @author Harald Musum + */ +public class UnicodeTest { + /** + * Reads a config from a file which is exactly like one returned from + * the config server given only default values for this config. + * The parsing code is the same whether the reading happens from file + * or from a server connection, so this tests that this config can be + * received correctly from the server + */ + @Test + public void testUnicodeConfigReading() { + ConfigGetter<UnicodeConfig> getter = new ConfigGetter<>(UnicodeConfig.class); + UnicodeConfig config = getter.getConfig("file:src/test/resources/configs/unicode/unicode.cfg"); + + assertEquals("Hei \u00E6\u00F8\u00E5 \uBC14\uB451 \u00C6\u00D8\u00C5 hallo", config.unicodestring1()); + assertEquals("abc \u00E6\u00F8\u00E5 \u56F2\u7881 \u00C6\u00D8\u00C5 ABC", config.unicodestring2()); + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/impl/FileConfigSubscriptionTest.java b/config/src/test/java/com/yahoo/config/subscription/impl/FileConfigSubscriptionTest.java new file mode 100644 index 00000000000..ce9323f0ec7 --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/impl/FileConfigSubscriptionTest.java @@ -0,0 +1,105 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription.impl; + +import com.yahoo.config.ConfigurationRuntimeException; +import com.yahoo.foo.SimpletypesConfig; +import com.yahoo.foo.TestReferenceConfig; +import com.yahoo.config.subscription.ConfigSubscriber; +import com.yahoo.config.subscription.DirSource; +import com.yahoo.config.subscription.FileSource; +import com.yahoo.vespa.config.ConfigKey; +import com.yahoo.vespa.config.TimingValues; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author lulf + * @since 5.1.7 + */ +public class FileConfigSubscriptionTest { + private File TEST_TYPES_FILE; + + @Before + public void setUp() throws IOException { + TEST_TYPES_FILE = File.createTempFile("fooconfig", ".cfg"); + } + + private void writeConfig(String field, String value) throws IOException { + FileWriter writer = new FileWriter(TEST_TYPES_FILE); + writer.write(field + " " + value); + writer.close(); + } + + @Test + public void require_that_new_config_is_detected_in_time() throws IOException, InterruptedException { + writeConfig("intval", "23"); + ConfigSubscriber subscriber = new ConfigSubscriber(new FileSource(TEST_TYPES_FILE)); + ConfigSubscription<SimpletypesConfig> sub = new FileConfigSubscription<>( + new ConfigKey<>(SimpletypesConfig.class, ""), + subscriber, + TEST_TYPES_FILE); + assertTrue(sub.nextConfig(1000)); + assertThat(sub.config.intval(), is(23)); + Thread.sleep(1000); + writeConfig("intval", "33"); + assertTrue(sub.nextConfig(1000)); + assertThat(sub.config.intval(), is(33)); + } + + @Test + public void require_that_new_config_is_detected_on_reload() throws IOException { + writeConfig("intval", "23"); + ConfigSubscriber subscriber = new ConfigSubscriber(new FileSource(TEST_TYPES_FILE)); + ConfigSubscription<SimpletypesConfig> sub = new FileConfigSubscription<>( + new ConfigKey<>(SimpletypesConfig.class, ""), + subscriber, + TEST_TYPES_FILE); + assertTrue(sub.nextConfig(1000)); + assertThat(sub.config.intval(), is(23)); + writeConfig("intval", "33"); + sub.reload(1); + assertTrue(sub.nextConfig(1000)); + assertThat(sub.config.intval(), is(33)); + assertTrue(sub.isConfigChanged()); + assertTrue(sub.isGenerationChanged()); + sub.reload(2); + assertTrue(sub.nextConfig(1000)); + assertThat(sub.config.intval(), is(33)); + assertFalse(sub.isConfigChanged()); + assertTrue(sub.isGenerationChanged()); + } + + @Test + public void require_that_dir_config_id_reference_is_not_changed() { + final String cfgDir = "src/test/resources/configs/foo"; + final String cfgId = "dir:" + cfgDir; + final ConfigKey<TestReferenceConfig> key = new ConfigKey<>(TestReferenceConfig.class, cfgId); + ConfigSubscriber subscriber = new ConfigSubscriber(); + ConfigSubscription<TestReferenceConfig> sub = ConfigSubscription.get(key, subscriber, new DirSource(new File(cfgDir)), new TimingValues()); + assertTrue(sub.nextConfig(1000)); + assertThat(sub.config.configId(), is(cfgId)); + } + + @Test(expected = ConfigurationRuntimeException.class) + public void require_that_bad_file_throws_exception() throws IOException { + // A little trick to ensure that we can create the subscriber, but that we get an error when reading. + writeConfig("intval", "23"); + ConfigSubscriber subscriber = new ConfigSubscriber(new FileSource(TEST_TYPES_FILE)); + ConfigSubscription<SimpletypesConfig> sub = new FileConfigSubscription<>( + new ConfigKey<>(SimpletypesConfig.class, ""), + subscriber, + TEST_TYPES_FILE); + sub.reload(1); + assertTrue(TEST_TYPES_FILE.setReadable(false)); + sub.nextConfig(0); + } +} diff --git a/config/src/test/java/com/yahoo/config/subscription/impl/JRTConfigRequesterTest.java b/config/src/test/java/com/yahoo/config/subscription/impl/JRTConfigRequesterTest.java new file mode 100644 index 00000000000..7d3b86db2fe --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/impl/JRTConfigRequesterTest.java @@ -0,0 +1,356 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription.impl; + +import com.yahoo.foo.SimpletypesConfig; +import com.yahoo.config.subscription.ConfigSubscriber; +import com.yahoo.jrt.Request; +import com.yahoo.vespa.config.ConfigKey; +import com.yahoo.vespa.config.ErrorCode; +import com.yahoo.vespa.config.ErrorType; +import com.yahoo.vespa.config.TimingValues; +import com.yahoo.vespa.config.protocol.JRTServerConfigRequestV3; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @since 5.1.11 + */ +public class JRTConfigRequesterTest { + + @Test + public void testDelayCalculation() { + TimingValues defaultTimingValues = new TimingValues(); + Random random = new Random(0); // Use seed to make tests predictable + TimingValues timingValues = new TimingValues(defaultTimingValues, random); + + // transientFailures and fatalFailures are not set until after delay has been calculated, + // so 0 is the case for the first failure + int transientFailures = 0; + int fatalFailures = 0; + boolean configured = false; + + // First time failure, not configured + long delay = JRTConfigRequester.calculateFailedRequestDelay(ErrorType.TRANSIENT, + transientFailures, fatalFailures, timingValues, configured); + assertThat(delay, is(timingValues.getUnconfiguredDelay())); + transientFailures = 5; + delay = JRTConfigRequester.calculateFailedRequestDelay(ErrorType.TRANSIENT, + transientFailures, fatalFailures, timingValues, configured); + assertThat(delay, is((transientFailures + 1) * timingValues.getUnconfiguredDelay())); + transientFailures = 0; + + + delay = JRTConfigRequester.calculateFailedRequestDelay(ErrorType.FATAL, + transientFailures, fatalFailures, timingValues, configured); + assertTrue(delay > (1 - JRTConfigRequester.randomFraction) * timingValues.getFixedDelay()); + assertTrue(delay < (1 + JRTConfigRequester.randomFraction) * timingValues.getFixedDelay()); + assertThat(delay, is(5462L)); + + // First time failure, configured + configured = true; + + delay = JRTConfigRequester.calculateFailedRequestDelay(ErrorType.TRANSIENT, + transientFailures, fatalFailures, timingValues, configured); + assertThat(delay, is(timingValues.getConfiguredErrorDelay())); + + delay = JRTConfigRequester.calculateFailedRequestDelay(ErrorType.FATAL, + transientFailures, fatalFailures, timingValues, configured); + assertTrue(delay > (1 - JRTConfigRequester.randomFraction) * timingValues.getFixedDelay()); + assertTrue(delay < (1 + JRTConfigRequester.randomFraction) * timingValues.getFixedDelay()); + assertThat(delay, is(5663L)); + + + // nth time failure, not configured + fatalFailures = 1; + configured = false; + delay = JRTConfigRequester.calculateFailedRequestDelay(ErrorType.TRANSIENT, + transientFailures, fatalFailures, timingValues, configured); + assertThat(delay, is(timingValues.getUnconfiguredDelay())); + delay = JRTConfigRequester.calculateFailedRequestDelay(ErrorType.FATAL, + transientFailures, fatalFailures, timingValues, configured); + final long l = timingValues.getFixedDelay() + timingValues.getUnconfiguredDelay(); + assertTrue(delay > (1 - JRTConfigRequester.randomFraction) * l); + assertTrue(delay < (1 + JRTConfigRequester.randomFraction) * l); + assertThat(delay, is(5377L)); + + + // nth time failure, configured + fatalFailures = 1; + configured = true; + delay = JRTConfigRequester.calculateFailedRequestDelay(ErrorType.TRANSIENT, + transientFailures, fatalFailures, timingValues, configured); + assertThat(delay, is(timingValues.getConfiguredErrorDelay())); + delay = JRTConfigRequester.calculateFailedRequestDelay(ErrorType.FATAL, + transientFailures, fatalFailures, timingValues, configured); + final long l1 = timingValues.getFixedDelay() + timingValues.getConfiguredErrorDelay(); + assertTrue(delay > (1 - JRTConfigRequester.randomFraction) * l1); + assertTrue(delay < (1 + JRTConfigRequester.randomFraction) * l1); + assertThat(delay, is(20851L)); + + + // 1 more than max delay multiplier time failure, configured + fatalFailures = timingValues.getMaxDelayMultiplier() + 1; + configured = true; + delay = JRTConfigRequester.calculateFailedRequestDelay(ErrorType.TRANSIENT, + transientFailures, fatalFailures, timingValues, configured); + assertThat(delay, is(timingValues.getConfiguredErrorDelay())); + assertTrue(delay < timingValues.getMaxDelayMultiplier() * timingValues.getConfiguredErrorDelay()); + delay = JRTConfigRequester.calculateFailedRequestDelay(ErrorType.FATAL, + transientFailures, fatalFailures, timingValues, configured); + final long l2 = timingValues.getFixedDelay() + timingValues.getMaxDelayMultiplier() * timingValues.getConfiguredErrorDelay(); + assertTrue(delay > (1 - JRTConfigRequester.randomFraction) * l2); + assertTrue(delay < (1 + JRTConfigRequester.randomFraction) * l2); + assertThat(delay, is(163520L)); + } + + @Test + public void testDelay() { + TimingValues timingValues = new TimingValues(); + + // transientFailures and fatalFailures are not set until after delay has been calculated, + // so 0 is the case for the first failure + int transientFailures = 0; + int fatalFailures = 0; + + // First time failure, configured + long delay = JRTConfigRequester.calculateFailedRequestDelay(ErrorType.TRANSIENT, + transientFailures, fatalFailures, timingValues, true); + assertThat(delay, is(timingValues.getConfiguredErrorDelay())); + assertThat(delay, is((transientFailures + 1) * timingValues.getConfiguredErrorDelay())); + } + + @Test + public void testErrorTypes() { + List<Integer> transientErrors = Arrays.asList(com.yahoo.jrt.ErrorCode.CONNECTION, com.yahoo.jrt.ErrorCode.TIMEOUT); + List<Integer> fatalErrors = Arrays.asList(ErrorCode.UNKNOWN_CONFIG, ErrorCode.UNKNOWN_DEFINITION, ErrorCode.OUTDATED_CONFIG, + ErrorCode.UNKNOWN_DEF_MD5, ErrorCode.ILLEGAL_NAME, ErrorCode.ILLEGAL_VERSION, ErrorCode.ILLEGAL_CONFIGID, + ErrorCode.ILLEGAL_DEF_MD5, ErrorCode.ILLEGAL_CONFIG_MD5, ErrorCode.ILLEGAL_TIMEOUT, ErrorCode.INTERNAL_ERROR, + 9999); // unknown should also be fatal + for (Integer i : transientErrors) { + assertThat(ErrorType.getErrorType(i), is(ErrorType.TRANSIENT)); + } + for (Integer i : fatalErrors) { + assertThat(ErrorType.getErrorType(i), is(ErrorType.FATAL)); + } + } + + @Test + public void testFirstRequestAfterSubscribing() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + final TimingValues timingValues = getTestTimingValues(); + JRTConfigSubscription<SimpletypesConfig> sub = createSubscription(subscriber, timingValues); + + final MockConnection connection = new MockConnection(); + JRTConfigRequester requester = new JRTConfigRequester(connection, timingValues); + assertThat(requester.getConnectionPool(), is(connection)); + requester.request(sub); + final Request request = connection.getRequest(); + assertNotNull(request); + assertThat(connection.getNumberOfRequests(), is(1)); + JRTServerConfigRequestV3 receivedRequest = JRTServerConfigRequestV3.createFromRequest(request); + assertTrue(receivedRequest.validateParameters()); + assertThat(receivedRequest.getTimeout(), is(timingValues.getSubscribeTimeout())); + assertThat(requester.getFatalFailures(), is(0)); + assertThat(requester.getTransientFailures(), is(0)); + } + + @Test + public void testFatalError() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + final TimingValues timingValues = getTestTimingValues(); + + final MockConnection connection = new MockConnection(new ErrorResponseHandler()); + JRTConfigRequester requester = new JRTConfigRequester(connection, timingValues); + requester.request(createSubscription(subscriber, timingValues)); + waitUntilResponse(connection); + assertThat(requester.getFatalFailures(), is(1)); + assertThat(requester.getTransientFailures(), is(0)); + } + + @Test + public void testFatalErrorSubscribed() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + final TimingValues timingValues = getTestTimingValues(); + JRTConfigSubscription<SimpletypesConfig> sub = createSubscription(subscriber, timingValues); + sub.setConfig(config()); + sub.setGeneration(1L); + + final MockConnection connection = new MockConnection(new ErrorResponseHandler()); + JRTConfigRequester requester = new JRTConfigRequester(connection, timingValues); + requester.request(sub); + waitUntilResponse(connection); + assertThat(requester.getFatalFailures(), is(1)); + assertThat(requester.getTransientFailures(), is(0)); + } + + @Test + public void testTransientError() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + final TimingValues timingValues = getTestTimingValues(); + + final MockConnection connection = new MockConnection(new ErrorResponseHandler(com.yahoo.jrt.ErrorCode.TIMEOUT)); + JRTConfigRequester requester = new JRTConfigRequester(connection, timingValues); + requester.request(createSubscription(subscriber, timingValues)); + waitUntilResponse(connection); + assertThat(requester.getFatalFailures(), is(0)); + assertThat(requester.getTransientFailures(), is(1)); + } + + @Test + public void testTransientErrorSubscribed() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + final TimingValues timingValues = getTestTimingValues(); + JRTConfigSubscription<SimpletypesConfig> sub = createSubscription(subscriber, timingValues); + sub.setConfig(config()); + sub.setGeneration(1L); + + final MockConnection connection = new MockConnection(new ErrorResponseHandler(com.yahoo.jrt.ErrorCode.TIMEOUT)); + JRTConfigRequester requester = new JRTConfigRequester(connection, timingValues); + requester.request(sub); + waitUntilResponse(connection); + assertThat(requester.getFatalFailures(), is(0)); + assertThat(requester.getTransientFailures(), is(1)); + } + + @Test + public void testUnknownConfigDefinitionError() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + final TimingValues timingValues = getTestTimingValues(); + JRTConfigSubscription<SimpletypesConfig> sub = createSubscription(subscriber, timingValues); + sub.setConfig(config()); + sub.setGeneration(1L); + + final MockConnection connection = new MockConnection(new ErrorResponseHandler(ErrorCode.UNKNOWN_DEFINITION)); + JRTConfigRequester requester = new JRTConfigRequester(connection, timingValues); + assertThat(requester.getConnectionPool(), is(connection)); + requester.request(sub); + waitUntilResponse(connection); + assertThat(requester.getFatalFailures(), is(1)); + assertThat(requester.getTransientFailures(), is(0)); + // TODO Check that no further request was sent? + } + + @Test + public void testClosedSubscription() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + final TimingValues timingValues = getTestTimingValues(); + JRTConfigSubscription<SimpletypesConfig> sub = createSubscription(subscriber, timingValues); + sub.close(); + + final MockConnection connection = new MockConnection(new MockConnection.OKResponseHandler()); + JRTConfigRequester requester = new JRTConfigRequester(connection, timingValues); + requester.request(sub); + assertThat(connection.getNumberOfRequests(), is(1)); + // Check that no further request was sent? + try { + Thread.sleep(timingValues.getFixedDelay()*2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + assertThat(connection.getNumberOfRequests(), is(1)); + } + + @Test + public void testTimeout() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + final TimingValues timingValues = getTestTimingValues(); + JRTConfigSubscription<SimpletypesConfig> sub = createSubscription(subscriber, timingValues); + sub.close(); + + final MockConnection connection = new MockConnection( + new DelayedResponseHandler(timingValues.getSubscribeTimeout()), + 2); // fake that we have more than one source + JRTConfigRequester requester = new JRTConfigRequester(connection, timingValues); + requester.request(createSubscription(subscriber, timingValues)); + // Check that no further request was sent? + try { + Thread.sleep(timingValues.getFixedDelay()*2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + assertTrue(connection.getNumberOfFailovers() >= 1); + } + + private JRTConfigSubscription<SimpletypesConfig> createSubscription(ConfigSubscriber subscriber, TimingValues timingValues) { + return new JRTConfigSubscription<>( + new ConfigKey<>(SimpletypesConfig.class, "testid"), subscriber, null, timingValues); + } + + private SimpletypesConfig config() { + SimpletypesConfig.Builder builder = new SimpletypesConfig.Builder(); + return new SimpletypesConfig(builder); + } + + private void waitUntilResponse(MockConnection connection) { + int i = 0; + while (i < 1000 && connection.getRequest() == null) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + i++; + } + } + + public static TimingValues getTestTimingValues() { return new TimingValues( + 1000, // successTimeout + 500, // errorTimeout + 500, // initialTimeout + 2000, // subscribeTimeout + 250, // unconfiguredDelay + 500, // configuredErrorDelay + 250, // fixedDelay + 5); // maxDelayMultiplier + } + + private static class ErrorResponseHandler extends MockConnection.OKResponseHandler { + private final int errorCode; + + public ErrorResponseHandler() { + this(ErrorCode.INTERNAL_ERROR); + } + + public ErrorResponseHandler(int errorCode) { + this.errorCode = errorCode; + } + + @Override + public void run() { + System.out.println("Running error response handler"); + request().setError(errorCode, "error"); + requestWaiter().handleRequestDone(request()); + } + } + + private static class DelayedResponseHandler extends MockConnection.OKResponseHandler { + private final long waitTimeMilliSeconds; + + public DelayedResponseHandler(long waitTimeMilliSeconds) { + this.waitTimeMilliSeconds = waitTimeMilliSeconds; + } + + @Override + public void run() { + System.out.println("Running delayed response handler (waiting " + waitTimeMilliSeconds + + ") before responding"); + try { + Thread.sleep(waitTimeMilliSeconds); + } catch (InterruptedException e) { + e.printStackTrace(); + } + request().setError(com.yahoo.jrt.ErrorCode.TIMEOUT, "error"); + requestWaiter().handleRequestDone(request()); + } + } + +} diff --git a/config/src/test/java/com/yahoo/config/subscription/util/JsonHelper.java b/config/src/test/java/com/yahoo/config/subscription/util/JsonHelper.java new file mode 100644 index 00000000000..27ac1a1278c --- /dev/null +++ b/config/src/test/java/com/yahoo/config/subscription/util/JsonHelper.java @@ -0,0 +1,28 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.subscription.util; + +import com.google.common.base.Joiner; + +import static org.hamcrest.MatcherAssert.assertThat; +import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; + +/** + * @author Vegard Sjonfjell + */ +public class JsonHelper { + /** + * Convenience method to input JSON without escaping double quotes and newlines + * Each parameter represents a line of JSON encoded data + * The lines are joined with newline and single quotes are replaced with double quotes + */ + public static String inputJson(String... lines) { + return Joiner.on("\n").join(lines).replaceAll("'", "\""); + } + + /** + * Structurally compare two JSON encoded strings + */ + public static void assertJsonEquals(String inputJson, String expectedJson) { + assertThat(inputJson, sameJSONAs(expectedJson)); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigBuilderMergeTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigBuilderMergeTest.java new file mode 100644 index 00000000000..95ed049de6d --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/ConfigBuilderMergeTest.java @@ -0,0 +1,120 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import com.yahoo.foo.ArraytypesConfig; +import com.yahoo.config.subscription.ConfigInstanceUtil; +import com.yahoo.foo.SimpletypesConfig; +import com.yahoo.foo.StructtypesConfig; +import com.yahoo.foo.MaptypesConfig; +import org.junit.Test; + +import java.util.Arrays; + +import static com.yahoo.foo.MaptypesConfig.Innermap; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * SEO keywords: test override() on builders. overrideTest, testOverride + * + * @author lulf + * @since 5.1 + */ +public class ConfigBuilderMergeTest { + + private SimpletypesConfig.Builder createSimpleBuilder(String s, int i, long l, double d, boolean b) { + SimpletypesConfig.Builder builder = new SimpletypesConfig.Builder(); + builder.stringval(s); + builder.intval(i); + builder.longval(l); + builder.doubleval(d); + builder.boolval(b); + return builder; + } + + private ArraytypesConfig.Builder createArrayBuilder(String [] strings) { + ArraytypesConfig.Builder builder = new ArraytypesConfig.Builder(); + for (String str : strings) { + builder.stringarr(str); + } + return builder; + } + + private StructtypesConfig.Builder createSimpleStructBuilder(String name, String gender, String [] emails) { + StructtypesConfig.Builder builder = new StructtypesConfig.Builder(); + StructtypesConfig.Simple.Builder simpleBuilder = new StructtypesConfig.Simple.Builder(); + simpleBuilder.name(name); + simpleBuilder.gender(StructtypesConfig.Simple.Gender.Enum.valueOf(gender)); + simpleBuilder.emails(Arrays.asList(emails)); + builder.simple(simpleBuilder); + return builder; + } + + @Test + public void require_that_simple_fields_are_overwritten_on_merge() { + SimpletypesConfig.Builder b1 = createSimpleBuilder("foo", 2, 5, 4.3, false); + SimpletypesConfig.Builder b2 = createSimpleBuilder("bar", 3, 6, 3.3, true); + ConfigInstanceUtil.setValues(b1, b2); + SimpletypesConfig c1 = new SimpletypesConfig(b1); + SimpletypesConfig c2 = new SimpletypesConfig(b2); + assertThat(c1, is(c2)); + } + + @Test + public void require_that_arrays_are_appended_on_merge() { + ArraytypesConfig.Builder b1 = createArrayBuilder(new String[] { "foo", "bar" }); + ArraytypesConfig.Builder b2 = createArrayBuilder(new String[] { "baz", "bim" }); + + ConfigInstanceUtil.setValues(b1, b2); + ArraytypesConfig c1 = new ArraytypesConfig(b1); + assertThat(c1.stringarr().size(), is(4)); + assertThat(c1.stringarr(0), is("foo")); + assertThat(c1.stringarr(1), is("bar")); + assertThat(c1.stringarr(2), is("baz")); + assertThat(c1.stringarr(3), is("bim")); + + ArraytypesConfig c2 = new ArraytypesConfig(b2); + assertThat(c2.stringarr(0), is("baz")); + assertThat(c2.stringarr(1), is("bim")); + } + + @Test + public void require_that_struct_fields_are_overwritten() { + String name1 = "foo"; + String gender1 = "MALE"; + String emails1[] = { "foo@bar", "bar@foo" }; + String name2 = "bar"; + String gender2 = "FEMALE"; + String emails2[] = { "foo@bar", "bar@foo" }; + StructtypesConfig.Builder b1 = createSimpleStructBuilder(name1, gender1, emails1); + StructtypesConfig.Builder b2 = createSimpleStructBuilder(name2, gender2, emails2); + ConfigInstanceUtil.setValues(b1, b2); + StructtypesConfig c1 = new StructtypesConfig(b1); + assertThat(c1.simple().name(), is(name2)); + assertThat(c1.simple().gender().toString(), is(gender2)); + assertThat(c1.simple().emails(0), is(emails2[0])); + assertThat(c1.simple().emails(1), is(emails2[1])); + } + + @Test + public void source_map_is_copied_into_destination_map_on_merge() { + MaptypesConfig.Builder destination = new MaptypesConfig.Builder() + .intmap("one", 1) + .innermap("first", new Innermap.Builder() + .foo(1)); + + MaptypesConfig.Builder source = new MaptypesConfig.Builder() + .intmap("two", 2) + .innermap("second", new Innermap.Builder() + .foo(2)); + + ConfigInstanceUtil.setValues(destination, source); + + MaptypesConfig config = new MaptypesConfig(destination); + assertThat(config.intmap("one"), is(1)); + assertThat(config.intmap("two"), is(2)); + assertThat(config.innermap("first").foo(), is(1)); + assertThat(config.innermap("second").foo(), is(2)); + } + +} diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigCacheKeyTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigCacheKeyTest.java new file mode 100755 index 00000000000..5356acdfade --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/ConfigCacheKeyTest.java @@ -0,0 +1,38 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.*; + +/** + * + * @author <a href="musum@yahoo-inc.com">Harald Musum</a> + */ +public class ConfigCacheKeyTest { + @Test + public void testConfigCacheKey() { + final String defMd5 = "md5"; + final String defMd5_2 = "md5_2"; + + ConfigCacheKey k1 = new ConfigCacheKey("foo", "id", "ns", defMd5); + ConfigCacheKey k2 = new ConfigCacheKey("foo", "id", "ns", defMd5); + ConfigCacheKey k3 = new ConfigCacheKey("foo", "id", "ns", defMd5_2); + ConfigCacheKey k4 = new ConfigCacheKey("foo", "id", "ns_1", defMd5); + ConfigCacheKey k5 = new ConfigCacheKey("foo", "id", "ns_1", null); // test with null defMd5 + final ConfigKey<?> configKey = new ConfigKey<>("foo", "id", "ns"); + ConfigCacheKey k1_2 = new ConfigCacheKey(configKey, defMd5); + assertTrue(k1.equals(k1)); + assertTrue(k1.equals(k1_2)); + assertTrue(k1.equals(k2)); + assertFalse(k3.equals(k2)); + assertFalse(k4.equals(k1)); + assertThat(k1.hashCode(), is(k2.hashCode())); + assertThat(k1.getDefMd5(), is(defMd5)); + assertThat(k1.toString(), is(configKey.toString() + "," + defMd5)); + assertThat(k5.hashCode(), is(not(k1.hashCode()))); + } + +} diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionBuilderTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionBuilderTest.java new file mode 100644 index 00000000000..cb1cccc0139 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionBuilderTest.java @@ -0,0 +1,141 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import com.yahoo.config.codegen.CNode; +import com.yahoo.config.codegen.DefParser; +import org.junit.Test; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + + +/** + * Unit tests for ConfigDefinitionBuilder. + * + * @author <a href="musum@yahoo-inc.com">Harald Musum</a> + */ +public class ConfigDefinitionBuilderTest { + + private static final String TEST_DIR = "src/test/resources/configs/def-files"; + private static final String DEF_NAME = TEST_DIR + "/function-test.def"; + + + @Test + // TODO Test ranges + public void testCreateConfigDefinition() throws IOException, InterruptedException { + File defFile = new File(DEF_NAME); + DefParser defParser = new DefParser(defFile.getName(), new FileReader(defFile)); + CNode root = defParser.getTree(); + + ConfigDefinition def = ConfigDefinitionBuilder.createConfigDefinition(root); + + assertNotNull(def); + assertThat(def.getBoolDefs().size(), is(2)); + assertNull(def.getBoolDefs().get("bool_val").getDefVal()); + assertThat(def.getBoolDefs().get("bool_with_def").getDefVal(), is(false)); + + assertThat(def.getIntDefs().size(), is(2)); + assertNull(def.getIntDefs().get("int_val").getDefVal()); + assertThat(def.getIntDefs().get("int_with_def").getDefVal(), is(-545)); + + assertThat(def.getLongDefs().size(), is(2)); + assertNull(def.getLongDefs().get("long_val").getDefVal()); + assertThat(def.getLongDefs().get("long_with_def").getDefVal(), is(-50000000000L)); + + assertThat(def.getDoubleDefs().size(), is(2)); + assertNull(def.getDoubleDefs().get("double_val").getDefVal()); + assertThat(def.getDoubleDefs().get("double_with_def").getDefVal(), is(-6.43)); + + assertThat(def.getEnumDefs().size(), is(3)); + assertTrue(def.getEnumDefs().containsKey("enum_val")); + assertThat(def.getEnumDefs().get("enum_val").getVals().size(), is(3)); + assertThat(def.getEnumDefs().get("enum_val").getVals().get(0), is("FOO")); + assertThat(def.getEnumDefs().get("enum_val").getVals().get(1), is("BAR")); + assertThat(def.getEnumDefs().get("enum_val").getVals().get(2), is("FOOBAR")); + + assertTrue(def.getEnumDefs().containsKey("enumwithdef")); + assertThat(def.getEnumDefs().get("enumwithdef").getDefVal(), is("BAR2")); + + assertTrue(def.getEnumDefs().containsKey("onechoice")); + assertThat(def.getEnumDefs().get("onechoice").getDefVal(), is("ONLYFOO")); + + assertThat(def.getStringDefs().size(), is(2)); + assertNull(def.getStringDefs().get("string_val").getDefVal()); // The return value is a String, so null if no default value + assertThat(def.getStringDefs().get("stringwithdef").getDefVal(), is("foobar")); + + assertThat(def.getReferenceDefs().size(), is(2)); + assertNotNull(def.getReferenceDefs().get("refval")); + assertThat(def.getReferenceDefs().get("refwithdef").getDefVal(), is(":parent:")); + + assertThat(def.getFileDefs().size(), is(1)); + assertNotNull(def.getFileDefs().get("fileVal")); + + assertThat(def.getArrayDefs().size(), is(9)); + assertNotNull(def.getArrayDefs().get("boolarr")); + assertThat(def.getArrayDefs().get("boolarr").getTypeSpec().getType(), is("bool")); + + assertNotNull(def.getArrayDefs().get("enumarr")); + assertThat(def.getArrayDefs().get("enumarr").getTypeSpec().getType(), is("enum")); + assertThat(def.getArrayDefs().get("enumarr").getTypeSpec().getEnumVals().toString(), is("[ARRAY, VALUES]")); + + assertNotNull(def.getArrayDefs().get("refarr")); + assertThat(def.getArrayDefs().get("refarr").getTypeSpec().getType(), is("reference")); + + assertNotNull(def.getArrayDefs().get("fileArr")); + assertThat(def.getArrayDefs().get("fileArr").getTypeSpec().getType(), is("file")); + + assertThat(def.getStructDefs().size(), is(2)); + assertNotNull(def.getStructDefs().get("basicStruct")); + assertThat(def.getStructDefs().get("basicStruct").getStringDefs().size(), is(1)); + assertThat(def.getStructDefs().get("basicStruct").getStringDefs().get("foo").getDefVal(), is("basic")); + assertThat(def.getStructDefs().get("basicStruct").getIntDefs().size(), is(1)); + assertNull(def.getStructDefs().get("basicStruct").getIntDefs().get("bar").getDefVal()); + assertThat(def.getStructDefs().get("basicStruct").getArrayDefs().size(), is(1)); + assertThat(def.getStructDefs().get("basicStruct").getArrayDefs().get("intArr").getTypeSpec().getType(), is("int")); + + assertNotNull(def.getStructDefs().get("rootStruct")); + assertNotNull(def.getStructDefs().get("rootStruct").getStructDefs().get("inner0")); + assertNotNull(def.getStructDefs().get("rootStruct").getStructDefs().get("inner1")); + assertThat(def.getStructDefs().get("rootStruct").getInnerArrayDefs().size(), is(1)); + assertNotNull(def.getStructDefs().get("rootStruct").getInnerArrayDefs().get("innerArr")); + assertThat(def.getStructDefs().get("rootStruct").getInnerArrayDefs().get("innerArr").getStringDefs().size(), is(1)); + + assertThat(def.getInnerArrayDefs().size(), is(1)); + assertNotNull(def.getInnerArrayDefs().get("myarray")); + assertThat(def.getInnerArrayDefs().get("myarray").getIntDefs().get("intval").getDefVal(), is(14)); + assertThat(def.getInnerArrayDefs().get("myarray").getArrayDefs().size(), is(1)); + assertNotNull(def.getInnerArrayDefs().get("myarray").getArrayDefs().get("stringval")); + assertThat(def.getInnerArrayDefs().get("myarray").getArrayDefs().get("stringval").getTypeSpec().getType(), is("string")); + assertThat(def.getInnerArrayDefs().get("myarray").getEnumDefs().get("enumval").getDefVal(), is("TYPE")); + assertNull(def.getInnerArrayDefs().get("myarray").getReferenceDefs().get("refval").getDefVal()); + assertThat(def.getInnerArrayDefs().get("myarray").getInnerArrayDefs().size(), is(1)); + assertThat(def.getInnerArrayDefs().get("myarray").getInnerArrayDefs().get("anotherarray").getIntDefs().get("foo").getDefVal(), is(-4)); + assertNull(def.getInnerArrayDefs().get("myarray").getStructDefs().get("myStruct").getIntDefs().get("a").getDefVal()); + assertThat(def.getInnerArrayDefs().get("myarray").getStructDefs().get("myStruct").getIntDefs().get("b").getDefVal(), is(2)); + + // Maps + assertEquals(def.getLeafMapDefs().size(), 4); + assertEquals(def.getLeafMapDefs().get("intMap").getTypeSpec().getType(), "int"); + assertEquals(def.getLeafMapDefs().get("stringMap").getTypeSpec().getType(), "string"); + assertEquals(def.getStructMapDefs().size(), 1); + assertEquals(def.getStructMapDefs().get("myStructMap").getIntDefs().get("myInt").getDefVal(), null); + assertEquals(def.getStructMapDefs().get("myStructMap").getStringDefs().get("myString").getDefVal(), null); + assertEquals(def.getStructMapDefs().get("myStructMap").getIntDefs().get("myIntDef").getDefVal(), (Integer)56); + assertEquals(def.getStructMapDefs().get("myStructMap").getStringDefs().get("myStringDef").getDefVal(), "g"); + + } + + @Test + public void testCreateConfigDefinitionOverrideNamespace() throws IOException, InterruptedException { + File defFile = new File(DEF_NAME); + DefParser defParser = new DefParser(defFile.getName(), new FileReader(defFile)); + CNode root = defParser.getTree(); + + ConfigDefinition def = ConfigDefinitionBuilder.createConfigDefinition(root, "foo"); + assertThat(def.getNamespace(), is("foo")); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionKeyTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionKeyTest.java new file mode 100644 index 00000000000..fbdca4af6df --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionKeyTest.java @@ -0,0 +1,42 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import org.junit.Test; + +import static junit.framework.TestCase.assertFalse; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + + +/** + * Tests ConfigDefinitionKey + * + * @author musum + */ +public class ConfigDefinitionKeyTest { + + @Test + public void testBasic() { + ConfigDefinitionKey def1 = new ConfigDefinitionKey("foo", ""); + ConfigDefinitionKey def2 = new ConfigDefinitionKey("foo", "bar"); + + assertThat(def1.getName(), is("foo")); + assertThat(def1.getNamespace(), is("")); + + assertTrue(def1.equals(def1)); + assertFalse(def1.equals(def2)); + assertFalse(def1.equals(new Object())); + assertTrue(def2.equals(def2)); + } + + @Test + public void testCreationFromConfigKey() { + ConfigKey<?> key1 = new ConfigKey<>("foo", "id", "bar"); + ConfigDefinitionKey def1 = new ConfigDefinitionKey(key1); + + assertThat(def1.getName(), is(key1.getName())); + assertThat(def1.getNamespace(), is(key1.getNamespace())); + } + +} diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionSetTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionSetTest.java new file mode 100644 index 00000000000..a00c013109e --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionSetTest.java @@ -0,0 +1,57 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import org.junit.Test; + +import static junit.framework.TestCase.assertNull; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + + +/** + * Class to hold config definitions and resolving requests for the correct definition + * + * @author Harald Musum <musum@yahoo-inc.com> + * @since 2011-11-18 + */ +public class ConfigDefinitionSetTest { + + @Test + public void testBasic() { + ConfigDefinitionSet configDefinitionSet = new ConfigDefinitionSet(); + ConfigDefinition def1 = new ConfigDefinition("foo", "1"); + ConfigDefinition def2 = new ConfigDefinition("foo", "1", "namespace1"); + ConfigDefinition def3 = new ConfigDefinition("foo", "1", "namespace2"); + final ConfigDefinitionKey key1 = new ConfigDefinitionKey(def1.getName(), def1.getNamespace()); + configDefinitionSet.add(key1, def1); + ConfigDefinitionKey key2 = new ConfigDefinitionKey(def2.getName(), def2.getNamespace()); + configDefinitionSet.add(key2, def2); + ConfigDefinitionKey key3 = new ConfigDefinitionKey(def3.getName(), def3.getNamespace()); + configDefinitionSet.add(key3, def3); + assertThat(configDefinitionSet.size(), is(3)); + assertThat(configDefinitionSet.get(key1), is(def1)); + assertThat(configDefinitionSet.get(key2), is(def2)); + assertThat(configDefinitionSet.get(key3), is(def3)); + + String str = configDefinitionSet.toString(); + assertTrue(str.contains("namespace1.foo")); + assertTrue(str.contains("namespace2.foo")); + assertTrue(str.contains("config.foo")); + } + + @Test + public void testFallbackToDefaultNamespace() { + ConfigDefinitionSet configDefinitionSet = new ConfigDefinitionSet(); + ConfigDefinition def1 = new ConfigDefinition("foo", "1"); + ConfigDefinition def2 = new ConfigDefinition("bar", "1", "namespace"); + + configDefinitionSet.add(new ConfigDefinitionKey(def1.getName(), def1.getNamespace()), def1); + configDefinitionSet.add(new ConfigDefinitionKey(def2.getName(), def2.getNamespace()), def2); + + // fallback to default namespace + assertThat(configDefinitionSet.get(new ConfigDefinitionKey("foo", "namespace")), is(def1)); + // Should not fallback to some other config with same name, but different namespace (not default namespace) + assertNull(configDefinitionSet.get(new ConfigDefinitionKey("bar", "someothernamespace"))); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionTest.java new file mode 100755 index 00000000000..ce03d5f6823 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/ConfigDefinitionTest.java @@ -0,0 +1,234 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import org.junit.Test; + +import com.yahoo.vespa.config.ConfigDefinition.EnumDef; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + +/** + * Unit tests for ConfigDefinition. + * + * @author <a href="musum@yahoo-inc.com">Harald Musum</a> + */ +public class ConfigDefinitionTest { + + @Test + public void testVersionComparator() { + Comparator<String> c = new ConfigDefinition.VersionComparator(); + + assertEquals(0, c.compare("1", "1")); + assertEquals(0, c.compare("1-0", "1")); + assertEquals(0, c.compare("1-0-0", "1")); + assertEquals(0, c.compare("1-0-0", "1-0")); + assertEquals(0, c.compare("0-1-1", "0-1-1")); + assertEquals(0, c.compare("0-1-0", "0-1")); + assertEquals(-1, c.compare("0", "1")); + assertEquals(-1, c.compare("0-1-0", "0-1-1")); + assertEquals(-1, c.compare("0-1-0", "1-1-1")); + assertEquals(-1, c.compare("0-0-1", "0-1")); + assertEquals(1, c.compare("0-1-1", "0-1-0")); + assertEquals(1, c.compare("1-1-1", "0-1-0")); + assertEquals(1, c.compare("0-1", "0-0-1")); + assertEquals(1, c.compare("1-1", "1")); + + List<String> versions = Arrays.asList("25", "5", "1-1", "0-2-3", "1", "1-0"); + Collections.sort(versions, new ConfigDefinition.VersionComparator()); + List<String> solution = Arrays.asList("0-2-3", "1", "1-0", "1-1", "5", "25"); + assertEquals(solution, versions); + } + + @Test + public void testDefNumberCompare() { + ConfigDefinition df1 = new ConfigDefinition("d", "25"); + ConfigDefinition df2 = new ConfigDefinition("d", "5"); + ConfigDefinition df3 = new ConfigDefinition("d", "1-1"); + ConfigDefinition df4 = new ConfigDefinition("d", "0-2-3"); + ConfigDefinition df5 = new ConfigDefinition("d", "1"); + ConfigDefinition df6 = new ConfigDefinition("d", "1-0"); + assertTrue(df1.compareTo(df2) > 0); + assertTrue(df2.compareTo(df4) > 0); + assertEquals(1, df3.compareTo(df4)); + assertEquals(-1, df4.compareTo(df5)); + assertEquals(0, df5.compareTo(df6)); + } + + @Test + public void testIntDefaultValues() { + ConfigDefinition def = new ConfigDefinition("foo", "1"); + + def.addIntDef("foo"); + def.addIntDef("bar", 0); + def.addIntDef("baz", 1); + def.addIntDef("xyzzy", 2, 0, null); + + assertNull(def.getIntDefs().get("foo").getDefVal()); + assertThat(def.getIntDefs().get("foo").getMin(), is(ConfigDefinition.INT_MIN)); + assertThat(def.getIntDefs().get("foo").getMax(), is(ConfigDefinition.INT_MAX)); + assertThat(def.getIntDefs().get("bar").getDefVal(), is(0)); + assertThat(def.getIntDefs().get("baz").getDefVal(), is(1)); + + assertThat(def.getIntDefs().get("xyzzy").getDefVal(), is(2)); + assertThat(def.getIntDefs().get("xyzzy").getMin(), is(0)); + assertThat(def.getIntDefs().get("xyzzy").getMax(), is(ConfigDefinition.INT_MAX)); + } + + @Test + public void testLongDefaultValues() { + ConfigDefinition def = new ConfigDefinition("foo", "1"); + + def.addLongDef("foo"); + def.addLongDef("bar", 1234567890123L); + def.addLongDef("xyzzy", 2L, 0L, null); + + assertNull(def.getLongDefs().get("foo").getDefVal()); + assertThat(def.getLongDefs().get("foo").getMin(), is(ConfigDefinition.LONG_MIN)); + assertThat(def.getLongDefs().get("foo").getMax(), is(ConfigDefinition.LONG_MAX)); + assertThat(def.getLongDefs().get("bar").getDefVal(), is(1234567890123L)); + + assertThat(def.getLongDefs().get("xyzzy").getDefVal(), is(2L)); + assertThat(def.getLongDefs().get("xyzzy").getMin(), is(0L)); + assertThat(def.getLongDefs().get("xyzzy").getMax(), is(ConfigDefinition.LONG_MAX)); + } + + @Test + @SuppressWarnings("serial") + public void testDefaultsPayloadMap() { + ConfigDefinition def = new ConfigDefinition("foo", "1"); + def.addStringDef("mystring"); + def.addStringDef("mystringdef", "foo"); + def.addBoolDef("mybool"); + def.addBoolDef("mybooldef", true); + def.addIntDef("myint"); + def.addIntDef("myintdef", 1); + def.addLongDef("mylong"); + def.addLongDef("mylongdef", 11l); + def.addDoubleDef("mydouble"); + def.addDoubleDef("mydoubledef", 2d); + EnumDef ed = new EnumDef(new ArrayList<String>(){{add("a1"); add("a2");}}, null); + EnumDef eddef = new EnumDef(new ArrayList<String>(){{add("a11"); add("a22");}}, "a22"); + def.addEnumDef("myenum", ed); + def.addEnumDef("myenumdef", eddef); + def.addReferenceDef("myref"); + def.addReferenceDef("myrefdef", "reff"); + def.addFileDef("myfile"); + def.addFileDef("myfiledef", "etc"); + } + + @Test + public void testVerification() { + ConfigDefinition def = new ConfigDefinition("foo", "1", "bar"); + def.addBoolDef("boolval"); + def.addStringDef("stringval"); + def.addIntDef("intval"); + def.addLongDef("longval"); + def.addDoubleDef("doubleval"); + def.addEnumDef("enumval", new EnumDef(Arrays.asList("FOO"), "FOO")); + def.addReferenceDef("refval"); + def.addFileDef("fileval"); + def.addInnerArrayDef("innerarr"); + def.addLeafMapDef("leafmap"); + ConfigDefinition.ArrayDef intArray = def.arrayDef("intArray"); + intArray.setTypeSpec(new ConfigDefinition.TypeSpec("intArray", "int", null, null, Integer.MIN_VALUE, Integer.MAX_VALUE)); + + ConfigDefinition.ArrayDef longArray = def.arrayDef("longArray"); + longArray.setTypeSpec(new ConfigDefinition.TypeSpec("longArray", "long", null, null, Long.MIN_VALUE, Long.MAX_VALUE)); + + ConfigDefinition.ArrayDef doubleArray = def.arrayDef("doubleArray"); + doubleArray.setTypeSpec(new ConfigDefinition.TypeSpec("doubleArray", "double", null, null, Double.MIN_VALUE, Double.MAX_VALUE)); + + ConfigDefinition.ArrayDef enumArray = def.arrayDef("enumArray"); + enumArray.setTypeSpec(new ConfigDefinition.TypeSpec("enumArray", "enum", null, "VALID", null, null)); + + ConfigDefinition.ArrayDef stringArray = def.arrayDef("stringArray"); + stringArray.setTypeSpec(new ConfigDefinition.TypeSpec("stringArray", "string", null, null, null, null)); + + def.structDef("struct"); + + assertVerify(def, "boolval", "true", 0); + assertVerify(def, "boolval", "false", 0); + assertVerify(def, "boolval", "invalid", IllegalArgumentException.class); + + + assertVerify(def, "stringval", "foobar", 0); + assertVerify(def, "stringval", "foobar", 0); + assertVerify(def, "intval", "123", 0); + assertVerify(def, "intval", "foobar", IllegalArgumentException.class); + assertVerify(def, "longval", "1234", 0); + assertVerify(def, "longval", "foobar", IllegalArgumentException.class); + assertVerify(def, "doubleval", "foobar", IllegalArgumentException.class); + assertVerify(def, "doubleval", "3", 0); + assertVerify(def, "doubleval", "3.14", 0); + assertVerify(def, "enumval", "foobar", IllegalArgumentException.class); + assertVerify(def, "enumval", "foo", IllegalArgumentException.class); + assertVerify(def, "enumval", "FOO", 0); + assertVerify(def, "refval", "foobar", 0); + assertVerify(def, "fileval", "foobar", 0); + + assertVerifyComplex(def, "innerarr", 0); + assertVerifyComplex(def, "leafmap", 0); + assertVerifyComplex(def, "intArray", 0); + assertVerifyComplex(def, "longArray", 0); + assertVerifyComplex(def, "doubleArray", 0); + assertVerifyComplex(def, "enumArray", 0); + assertVerifyComplex(def, "stringArray", 0); + assertVerifyArray(intArray, "1345", 0, 0); + assertVerifyArray(intArray, "invalid", 0, IllegalArgumentException.class); + assertVerifyArray(longArray, "1345", 0, 0); + assertVerifyArray(longArray, "invalid", 0, IllegalArgumentException.class); + assertVerifyArray(doubleArray, "1345", 0, 0); + assertVerifyArray(doubleArray, "1345.3", 0, 0); + assertVerifyArray(doubleArray, "invalid", 0, IllegalArgumentException.class); + assertVerifyArray(enumArray, "valid", 0, IllegalArgumentException.class); + assertVerifyArray(enumArray, "VALID", 0, 0); + assertVerifyArray(enumArray, "inVALID", 0, IllegalArgumentException.class); + assertVerifyArray(stringArray, "VALID", 0, 0); + assertVerifyComplex(def, "struct", 0); + } + + private void assertVerifyArray(ConfigDefinition.ArrayDef def, String val, int index, int expectedNumWarnings) { + List<String> issuedWarnings = new ArrayList<>(); + def.verify(val, index, issuedWarnings); + assertThat(issuedWarnings.size(), is(expectedNumWarnings)); + } + + private void assertVerifyArray(ConfigDefinition.ArrayDef def, String val, int index, Class<?> expectedException) { + try { + def.verify(val, index, new ArrayList<String>()); + } catch (Exception e) { + if (!(e.getClass().isAssignableFrom(expectedException))) { + throw e; + } + } + } + + private void assertVerify(ConfigDefinition def, String id, String val, int expectedNumWarnings) { + List<String> issuedWarnings = new ArrayList<>(); + def.verify(id, val, issuedWarnings); + assertThat(issuedWarnings.size(), is(expectedNumWarnings)); + } + + private void assertVerify(ConfigDefinition def, String id, String val, Class<?> expectedException) { + try { + def.verify(id, val, new ArrayList<String>()); + } catch (Exception e) { + if (!(e.getClass().isAssignableFrom(expectedException))) { + throw e; + } + } + } + + private void assertVerifyComplex(ConfigDefinition def, String id, int expectedNumWarnings) { + List<String> issuedWarnings = new ArrayList<>(); + def.verify(id, issuedWarnings); + assertThat(issuedWarnings.size(), is(expectedNumWarnings)); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigFileFormatterTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigFileFormatterTest.java new file mode 100644 index 00000000000..0e55714ca53 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/ConfigFileFormatterTest.java @@ -0,0 +1,336 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import com.yahoo.foo.ArraytypesConfig; +import com.yahoo.foo.SimpletypesConfig; +import com.yahoo.foo.StructtypesConfig; +import com.yahoo.config.codegen.DefParser; +import com.yahoo.config.codegen.InnerCNode; +import com.yahoo.foo.MaptypesConfig; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.Slime; +import com.yahoo.text.StringUtilities; +import com.yahoo.text.Utf8; +import org.junit.Test; +import org.junit.Ignore; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringReader; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +/** + * @author lulf + * @since 5.1 + */ +public class ConfigFileFormatterTest { + + private String expected_simpletypes = "stringval \"foo\"\n" + + "intval 324234\n" + + "longval 324\n" + + "doubleval 3.455\n" + + "enumval VAL2\n" + + "boolval true\n"; + + @Test + public void require_that_basic_formatting_is_correct() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("stringval", "foo"); + root.setString("intval", "324234"); + root.setString("longval", "324"); + root.setString("doubleval", "3.455"); + root.setString("enumval", "VAL2"); + root.setString("boolval", "true"); + + assertConfigFormat(slime, expected_simpletypes); + } + + @Test + public void require_that_basic_formatting_is_correct_with_types() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("stringval", "foo"); + root.setLong("intval", 324234); + root.setLong("longval", 324); + root.setDouble("doubleval", 3.455); + root.setString("enumval", "VAL2"); + root.setBool("boolval", true); + + assertConfigFormat(slime, expected_simpletypes); + } + + private void assertConfigFormat(Slime slime, String expected_simpletypes) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InnerCNode def = new DefParser("simpletypes", new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + new ConfigFileFormat(def).encode(baos, slime); + assertThat(baos.toString(), is(expected_simpletypes)); + } + + @Test + public void require_that_field_not_found_is_ignored() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("nosuchfield", "bar"); + InnerCNode def = new DefParser("simpletypes", new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + new ConfigFileFormat(def).encode(baos, slime); + assertThat(baos.toString(), is("")); + } + + // TODO: Reenable this when we can reenable typechecking. + @Ignore + @Test(expected = IllegalArgumentException.class) + public void require_that_illegal_int_throws_exception() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("intval", "invalid"); + InnerCNode def = new DefParser("simpletypes", new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + new ConfigFileFormat(def).encode(new ByteArrayOutputStream(), slime); + } + + // TODO: Reenable this when we can reenable typechecking. + @Ignore + @Test(expected = IllegalArgumentException.class) + public void require_that_illegal_long_throws_exception() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("longval", "invalid"); + InnerCNode def = new DefParser("simpletypes", new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + new ConfigFileFormat(def).encode(new ByteArrayOutputStream(), slime); + } + + // TODO: Reenable this when we can reenable typechecking. + @Ignore + @Test(expected = IllegalArgumentException.class) + public void require_that_illegal_double_throws_exception() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("doubleval", "invalid"); + InnerCNode def = new DefParser("simpletypes", new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + new ConfigFileFormat(def).encode(new ByteArrayOutputStream(), slime); + } + + @Test + public void require_that_illegal_boolean_becomes_false() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("boolval", "invalid"); + InnerCNode def = new DefParser("simpletypes", new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + new ConfigFileFormat(def).encode(baos, slime); + assertThat(baos.toString(), is("boolval false\n")); + } + + // TODO: Remove this when we can reenable typechecking. + @Test + public void require_that_types_are_not_checked() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("enumval", "null"); + root.setString("intval", "null"); + root.setString("longval", "null"); + root.setString("boolval", "null"); + root.setString("doubleval", "null"); + InnerCNode def = new DefParser("simpletypes", new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + new ConfigFileFormat(def).encode(baos, slime); + assertThat(baos.toString("UTF-8"), is("enumval null\nintval null\nlongval null\nboolval false\ndoubleval null\n")); + } + + // TODO: Reenable this when we can reenable typechecking. + @Ignore + @Test(expected = IllegalArgumentException.class) + public void require_that_illegal_enum_throws_exception() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("enumval", "invalid"); + InnerCNode def = new DefParser("simpletypes", new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + new ConfigFileFormat(def).encode(new ByteArrayOutputStream(), slime); + } + + @Test + public void require_that_strings_are_encoded() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + String value = "\u7d22"; + root.setString("stringval", value); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InnerCNode def = new DefParser("simpletypes", new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + new ConfigFileFormat(def).encode(baos, slime); + assertThat(baos.toString("UTF-8"), is("stringval \"" + value + "\"\n")); + } + + @Test + public void require_that_array_formatting_is_correct() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + Cursor boolarr = root.setArray("boolarr"); + boolarr.addString("true"); + boolarr.addString("false"); + Cursor doublearr = root.setArray("doublearr"); + doublearr.addString("3.14"); + doublearr.addString("1.414"); + Cursor enumarr = root.setArray("enumarr"); + enumarr.addString("VAL1"); + enumarr.addString("VAL2"); + Cursor intarr = root.setArray("intarr"); + intarr.addString("3"); + intarr.addString("5"); + Cursor longarr = root.setArray("longarr"); + longarr.addString("55"); + longarr.addString("66"); + Cursor stringarr = root.setArray("stringarr"); + stringarr.addString("foo"); + stringarr.addString("bar"); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InnerCNode def = new DefParser("arraytypes", new StringReader(StringUtilities.implode(ArraytypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + new ConfigFileFormat(def).encode(baos, slime); + assertThat(baos.toString(), is( + "boolarr[0] true\n" + + "boolarr[1] false\n" + + "doublearr[0] 3.14\n" + + "doublearr[1] 1.414\n" + + "enumarr[0] VAL1\n" + + "enumarr[1] VAL2\n" + + "intarr[0] 3\n" + + "intarr[1] 5\n" + + "longarr[0] 55\n" + + "longarr[1] 66\n" + + "stringarr[0] \"foo\"\n" + + "stringarr[1] \"bar\"\n")); + } + + @Test + public void require_that_map_formatting_is_correct() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + Cursor boolval = root.setObject("boolmap"); + boolval.setString("foo", "true"); + boolval.setString("bar", "false"); + root.setObject("intmap").setString("foo", "1234"); + root.setObject("longmap").setString("foo", "12345"); + root.setObject("doublemap").setString("foo", "3.14"); + root.setObject("stringmap").setString("foo", "bar"); + root.setObject("innermap").setObject("bar").setString("foo", "1234"); + root.setObject("nestedmap").setObject("baz").setObject("inner").setString("foo", "1234"); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InnerCNode def = new DefParser("maptypes", new StringReader(StringUtilities.implode(MaptypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + new ConfigFileFormat(def).encode(baos, slime); + assertThat(baos.toString(), is( + "boolmap{\"foo\"} true\n" + + "boolmap{\"bar\"} false\n" + + "intmap{\"foo\"} 1234\n" + + "longmap{\"foo\"} 12345\n" + + "doublemap{\"foo\"} 3.14\n" + + "stringmap{\"foo\"} \"bar\"\n" + + "innermap{\"bar\"}.foo 1234\n" + + "nestedmap{\"baz\"}.inner{\"foo\"} 1234\n")); + } + + @Test + public void require_that_struct_formatting_is_correct() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + Cursor simple = root.setObject("simple"); + simple.setString("name", "myname"); + simple.setString("gender", "FEMALE"); + Cursor array = simple.setArray("emails"); + array.addString("foo@bar.com"); + array.addString("bar@baz.net"); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InnerCNode def = new DefParser("structtypes", new StringReader(StringUtilities.implode(StructtypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + new ConfigFileFormat(def).encode(baos, slime); + assertThat(baos.toString(), is( + "simple.name \"myname\"\n" + + "simple.gender FEMALE\n" + + "simple.emails[0] \"foo@bar.com\"\n" + + "simple.emails[1] \"bar@baz.net\"\n" + )); + } + + @Test + public void require_that_complex_struct_formatting_is_correct() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + + Cursor nested = root.setObject("nested"); + Cursor nested_inner = nested.setObject("inner"); + nested_inner.setString("name", "baz"); + nested_inner.setString("gender", "FEMALE"); + Cursor nested_inner_arr = nested_inner.setArray("emails"); + nested_inner_arr.addString("foo"); + nested_inner_arr.addString("bar"); + + Cursor nestedarr = root.setArray("nestedarr"); + Cursor nestedarr1 = nestedarr.addObject(); + Cursor inner1 = nestedarr1.setObject("inner"); + inner1.setString("name", "foo"); + inner1.setString("gender", "FEMALE"); + Cursor inner1arr = inner1.setArray("emails"); + inner1arr.addString("foo@bar"); + inner1arr.addString("bar@foo"); + + Cursor complexarr = root.setArray("complexarr"); + Cursor complexarr1 = complexarr.addObject(); + Cursor innerarr1 = complexarr1.setArray("innerarr"); + Cursor innerarr11 = innerarr1.addObject(); + innerarr11.setString("name", "bar"); + innerarr11.setString("gender", "MALE"); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InnerCNode def = new DefParser("structtypes", new StringReader(StringUtilities.implode(StructtypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + new ConfigFileFormat(def).encode(baos, slime); + assertThat(baos.toString(), is( + "nested.inner.name \"baz\"\n" + + "nested.inner.gender FEMALE\n" + + "nested.inner.emails[0] \"foo\"\n" + + "nested.inner.emails[1] \"bar\"\n" + + "nestedarr[0].inner.name \"foo\"\n" + + "nestedarr[0].inner.gender FEMALE\n" + + "nestedarr[0].inner.emails[0] \"foo@bar\"\n" + + "nestedarr[0].inner.emails[1] \"bar@foo\"\n" + + "complexarr[0].innerarr[0].name \"bar\"\n" + + "complexarr[0].innerarr[0].gender MALE\n" + )); + } + + @Test + public void require_that_strings_are_properly_escaped() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("stringval", "some\"quotes\\\"instring"); + InnerCNode def = new DefParser("simpletypes", new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + new ConfigFileFormat(def).encode(baos, slime); + assertThat(baos.toString(), is("stringval \"some\\\"quotes\\\\\\\"instring\"\n")); + } + + @Test + @Ignore + public void require_that_utf8_works() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + final String input = "Hei \u00E6\u00F8\u00E5 \n \uBC14\uB451 \u00C6\u00D8\u00C5 hallo"; + root.setString("stringval", input); + System.out.println(bytesToHexString(Utf8.toBytes(input))); + InnerCNode def = new DefParser("simpletypes", new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))).getTree(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + new ConfigFileFormat(def).encode(baos, slime); + System.out.println(bytesToHexString(baos.toByteArray())); + assertThat(Utf8.toString(baos.toByteArray()), is("stringval \"" + input + "\"\n")); + } + + public static String bytesToHexString(byte[] bytes){ + StringBuilder sb = new StringBuilder(); + for(byte b : bytes){ + sb.append(String.format("%02x", b&0xff)); + } + return sb.toString(); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigHelperTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigHelperTest.java new file mode 100644 index 00000000000..ce625727491 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/ConfigHelperTest.java @@ -0,0 +1,31 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import com.yahoo.config.subscription.ConfigSourceSet; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @since 5.1.9 + */ +public class ConfigHelperTest { + + @Test + public void basic() { + ConfigSourceSet configSourceSet = new ConfigSourceSet("host.com"); + ConfigHelper helper = new ConfigHelper(configSourceSet); + assertThat(helper.getConfigSourceSet(), is(configSourceSet)); + assertThat(helper.getConnectionPool().getAllSourceAddresses(), is("host.com")); + assertThat(helper.getTimingValues().getSubscribeTimeout(), is(new TimingValues().getSubscribeTimeout())); + + // Specify timing values + TimingValues tv = new TimingValues(); + tv.setSubscribeTimeout(11L); + helper = new ConfigHelper(configSourceSet, tv); + assertThat(helper.getTimingValues().getSubscribeTimeout(), is(tv.getSubscribeTimeout())); + } + +} diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigKeyTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigKeyTest.java new file mode 100644 index 00000000000..e824571d9d1 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/ConfigKeyTest.java @@ -0,0 +1,121 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.yahoo.foo.AppConfig; +import com.yahoo.config.ConfigurationRuntimeException; +import com.yahoo.config.codegen.CNode; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * + * @author <a href="musum@yahoo-inc.com">Harald Musum</a> + */ +public class ConfigKeyTest { + + @Test + public void testConfigId() { + String namespace = "bar"; + ConfigKey<?> key1 = new ConfigKey<>("foo", "a/b/c", namespace); + ConfigKey<?> key2 = new ConfigKey<>("foo", "a/b/c", namespace); + + assertEquals(key1, key2); + + ConfigKey<?> key3 = new ConfigKey<>("foo", "a/b/c/d", namespace); + assertTrue(!key1.equals(key3)); + assertFalse(key1.equals(key3)); + + assertEquals("a/b/c", new ConfigKey<>("foo", "a/b/c", namespace).getConfigId()); + assertEquals("a", new ConfigKey<>("foo", "a", namespace).getConfigId()); + assertEquals("", new ConfigKey<>("foo", "", namespace).getConfigId()); + + assertTrue(key1.equals(key1)); + assertFalse(key1.equals(key3)); + assertFalse(key1.equals(new Object())); + + ConfigKey<?> key4 = new ConfigKey<>("myConfig", null, namespace); + assertEquals("", key4.getConfigId()); + } + + @Test + public void testConfigKey() { + String name = AppConfig.CONFIG_DEF_NAME; + String namespace = AppConfig.CONFIG_DEF_NAMESPACE; + String md5 = AppConfig.CONFIG_DEF_MD5; + String configId = "myId"; + + ConfigKey<AppConfig> classKey = new ConfigKey<>(AppConfig.class, configId); + assertEquals("Name is set correctly from class", name, classKey.getName()); + assertEquals("Namespace is set correctly from class", namespace, classKey.getNamespace()); + assertEquals(configId, classKey.getConfigId()); + assertEquals("Md5 is set correctly from class", md5, classKey.getMd5()); + + ConfigKey<?> stringKey = new ConfigKey<>(name, configId, namespace); + assertEquals("Key created from class equals key created from strings", stringKey, classKey); + } + + @Test(expected = ConfigurationRuntimeException.class) + public void testNoName() { + new ConfigKey<>(null, "", ""); + } + + // Tests namespace and equals with combinations of namespace. + @Test + public void testNamespace() { + ConfigKey<?> noNamespace = new ConfigKey<>("name", "id", null); + ConfigKey<?> namespaceFoo = new ConfigKey<>("name", "id", "foo"); + ConfigKey<?> namespaceBar = new ConfigKey<>("name", "id", "bar"); + assertTrue(noNamespace.equals(noNamespace)); + assertTrue(namespaceFoo.equals(namespaceFoo)); + assertFalse(noNamespace.equals(namespaceFoo)); + assertFalse(namespaceFoo.equals(noNamespace)); + assertFalse(namespaceFoo.equals(namespaceBar)); + assertEquals(noNamespace.getNamespace(), CNode.DEFAULT_NAMESPACE); + assertEquals(namespaceBar.getNamespace(), "bar"); + } + + @Test + public void testSorting() { + ConfigKey<?> k1 = new ConfigKey<>("name3", "id2", "nsc"); + ConfigKey<?> k2 = new ConfigKey<>("name2", "id2", "nsb"); + ConfigKey<?> k3 = new ConfigKey<>("name1", "id2", "nsa"); + List<ConfigKey<?>> keys = new ArrayList<>(); + keys.add(k2); + keys.add(k1); + keys.add(k3); + Collections.sort(keys); + assertEquals(keys.get(0), k3); + assertEquals(keys.get(1), k2); + assertEquals(keys.get(2), k1); + + k1 = new ConfigKey<>("name2", "id2", "nsa"); + k2 = new ConfigKey<>("name3", "id3", "nsa"); + k3 = new ConfigKey<>("name1", "id4", "nsa"); + keys = new ArrayList<>(); + keys.add(k3); + keys.add(k2); + keys.add(k1); + Collections.sort(keys); + assertEquals(keys.get(0), k3); + assertEquals(keys.get(1), k1); + assertEquals(keys.get(2), k2); + + k1 = new ConfigKey<>("name", "idC", "nsa"); + k2 = new ConfigKey<>("name", "idA", "nsa"); + k3 = new ConfigKey<>("name", "idB", "nsa"); + keys = new ArrayList<>(); + keys.add(k1); + keys.add(k2); + keys.add(k3); + Collections.sort(keys); + assertEquals(keys.get(0), k2); + assertEquals(keys.get(1), k3); + assertEquals(keys.get(2), k1); + } + +} diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigPayloadBuilderTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigPayloadBuilderTest.java new file mode 100644 index 00000000000..24ade0f83c2 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/ConfigPayloadBuilderTest.java @@ -0,0 +1,363 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import com.yahoo.slime.Cursor; +import com.yahoo.slime.JsonFormat; +import com.yahoo.slime.Slime; +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; + +/** + * @author lulf + * @since 5.1 + */ +public class ConfigPayloadBuilderTest { + + private ConfigPayloadBuilder builderWithDef; + + private Cursor createSlime(ConfigPayloadBuilder builder) { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + builder.resolve(root); + return root; + } + + @Before + public void setupBuilder() { + ConfigDefinition def = new ConfigDefinition("foo", "1", "bar"); + def.addBoolDef("boolval"); + ConfigDefinition mystruct = def.structDef("mystruct"); + mystruct.addIntDef("foofield"); + def.arrayDef("myarray").setTypeSpec(new ConfigDefinition.TypeSpec("myarray", "int", null, null, null, null)); + ConfigDefinition myinnerarray = def.innerArrayDef("myinnerarray"); + myinnerarray.addIntDef("foo"); + builderWithDef = new ConfigPayloadBuilder(def, new ArrayList<String>()); + } + + @Test + public void require_that_simple_fields_are_set() { + ConfigPayloadBuilder builder = new ConfigPayloadBuilder(); + builder.setField("foo", "bar"); + builder.setField("bar", "barz"); + builder.getObject("bar").setValue("baz"); + Cursor root = createSlime(builder); + assertEquals("bar", root.field("foo").asString()); + assertEquals("baz", root.field("bar").asString()); + } + + @Test + public void require_that_simple_fields_can_be_overwritten() { + ConfigPayloadBuilder builder = new ConfigPayloadBuilder(); + builder.setField("foo", "bar"); + builder.setField("foo", "baz"); + Cursor root = createSlime(builder); + // XXX: Not sure if this is the _right_ behavior. + assertEquals("baz", root.field("foo").asString()); + } + + @Test + public void require_that_struct_values_are_created() { + ConfigPayloadBuilder builder = new ConfigPayloadBuilder(); + ConfigPayloadBuilder struct = builder.getObject("foo"); + struct.setField("bar", "baz"); + + Cursor root = createSlime(builder); + Cursor s = root.field("foo"); + assertEquals("baz", s.field("bar").asString()); + } + + + @Test + public void require_that_maps_are_created() { + ConfigPayloadBuilder builder = new ConfigPayloadBuilder(); + ConfigPayloadBuilder.MapBuilder map = builder.getMap("foo"); + assertNotNull(map); + } + + @Test + public void require_that_maps_support_simple_values() { + ConfigPayloadBuilder builder = new ConfigPayloadBuilder(); + ConfigPayloadBuilder.MapBuilder map = builder.getMap("foo"); + map.put("fookey", "foovalue"); + map.put("barkey", "barvalue"); + map.put("bazkey", "bazvalue"); + map.put("fookey", "lolvalue"); + assertThat(map.getElements().size(), is(3)); + Cursor root = createSlime(builder); + Cursor a = root.field("foo"); + assertThat(a.field("barkey").asString(), is("barvalue")); + assertThat(a.field("bazkey").asString(), is("bazvalue")); + assertThat(a.field("fookey").asString(), is("lolvalue")); + } + + @Test + public void require_that_arrays_are_created() { + ConfigPayloadBuilder builder = new ConfigPayloadBuilder(); + ConfigPayloadBuilder.Array array = builder.getArray("foo"); + assertNotNull(array); + } + + @Test + public void require_that_arrays_can_be_appended_simple_values() { + ConfigPayloadBuilder builder = new ConfigPayloadBuilder(); + ConfigPayloadBuilder.Array array = builder.getArray("foo"); + array.append("bar"); + array.append("baz"); + array.append("bim"); + assertThat(array.getElements().size(), is(3)); + Cursor root = createSlime(builder); + Cursor a = root.field("foo"); + assertEquals("bar", a.entry(0).asString()); + assertEquals("baz", a.entry(1).asString()); + assertEquals("bim", a.entry(2).asString()); + } + + @Test + public void require_that_arrays_can_be_indexed_simple_values() { + ConfigPayloadBuilder builder = new ConfigPayloadBuilder(); + ConfigPayloadBuilder.Array array = builder.getArray("foo"); + array.set(3, "bar"); + array.set(2, "baz"); + array.set(6, "bim"); + array.set(4, "bum"); + + Cursor root = createSlime(builder); + Cursor a = root.field("foo"); + assertEquals("bar", a.entry(0).asString()); + assertEquals("baz", a.entry(1).asString()); + assertEquals("bim", a.entry(2).asString()); + assertEquals("bum", a.entry(3).asString()); + } + + @Test + public void require_that_arrays_can_be_appended_structs() { + ConfigPayloadBuilder builder = new ConfigPayloadBuilder(); + ConfigPayloadBuilder.Array array = builder.getArray("foo"); + ConfigPayloadBuilder elem1 = array.append(); + elem1.setField("bar", "baz"); + ConfigPayloadBuilder elem2 = array.append(); + elem2.setField("foo", "bar"); + Cursor root = createSlime(builder); + Cursor a = root.field("foo"); + assertEquals("baz", a.entry(0).field("bar").asString()); + assertEquals("bar", a.entry(1).field("foo").asString()); + } + + @Test + public void require_that_arrays_can_be_indexed_structs() { + ConfigPayloadBuilder builder = new ConfigPayloadBuilder(); + ConfigPayloadBuilder.Array array = builder.getArray("foo"); + ConfigPayloadBuilder elem1 = array.set(4); + elem1.setField("bar", "baz"); + ConfigPayloadBuilder elem2 = array.set(2); + elem2.setField("foo", "bar"); + + Cursor root = createSlime(builder); + Cursor a = root.field("foo"); + assertEquals("baz", a.entry(0).field("bar").asString()); + assertEquals("bar", a.entry(1).field("foo").asString()); + } + + @Test + public void require_that_get_can_be_used_instead() { + ConfigPayloadBuilder builder = new ConfigPayloadBuilder(); + ConfigPayloadBuilder.Array array = builder.getArray("foo"); + // Causes append to be used + ConfigPayloadBuilder b1 = array.get(0); + ConfigPayloadBuilder b2 = array.get(1); + ConfigPayloadBuilder b3 = array.get(0); + ConfigPayloadBuilder b4 = array.get(1); + assertThat(b1, is(b3)); + assertThat(b2, is(b4)); + + ConfigPayloadBuilder.Array array_indexed = builder.getArray("bar"); + ConfigPayloadBuilder bi3 = array_indexed.set(3); + ConfigPayloadBuilder bi1 = array_indexed.set(1); + ConfigPayloadBuilder bi32 = array_indexed.get(3); + ConfigPayloadBuilder bi12 = array_indexed.get(1); + assertThat(bi12, is(bi1)); + assertThat(bi32, is(bi3)); + } + + @Test + public void require_that_builders_can_be_merged() { + ConfigPayloadBuilder b1 = new ConfigPayloadBuilder(); + ConfigPayloadBuilder b2 = new ConfigPayloadBuilder(); + ConfigPayloadBuilder b3 = new ConfigPayloadBuilder(); + b1.setField("aaa", "a"); + b1.getObject("bbb").setField("ccc", "ddd"); + + b2.setField("aaa", "b"); + b2.getObject("bbb").setField("ccc", "eee"); + b2.getArray("eee").append("kkk"); + b2.setField("uuu", "ttt"); + + b3.setField("aaa", "c"); + b3.getObject("bbb").setField("ccc", "fff"); + b3.getArray("eee").append("lll"); + b3.setField("uuu", "vvv"); + + assertThat(b1.override(b2), is(b1)); + assertThat(b1.override(b3), is(b1)); + + Cursor b1root = createSlime(b1); + Cursor b2root = createSlime(b2); + Cursor b3root = createSlime(b3); + assertThat(b3root.field("aaa").asString(), is("c")); + assertThat(b3root.field("bbb").field("ccc").asString(), is("fff")); + assertThat(b3root.field("eee").children(), is(1)); + assertThat(b3root.field("eee").entry(0).asString(), is("lll")); + assertThat(b3root.field("uuu").asString(), is("vvv")); + + assertThat(b2root.field("aaa").asString(), is("b")); + assertThat(b2root.field("bbb").field("ccc").asString(), is("eee")); + assertThat(b2root.field("eee").children(), is(1)); + assertThat(b2root.field("eee").entry(0).asString(), is("kkk")); + assertThat(b2root.field("uuu").asString(), is("ttt")); + + assertThat(b1root.field("aaa").asString(), is("c")); + assertThat(b1root.field("bbb").field("ccc").asString(), is("fff")); + assertThat(b1root.field("eee").children(), is(2)); + assertThat(b1root.field("eee").entry(0).asString(), is("kkk")); + assertThat(b1root.field("eee").entry(1).asString(), is("lll")); + assertThat(b1root.field("uuu").asString(), is("vvv")); + } + + @Test(expected=IllegalStateException.class) + public void require_that_append_conflicts_with_index() { + ConfigPayloadBuilder builder = new ConfigPayloadBuilder(); + ConfigPayloadBuilder.Array array = builder.getArray("foo"); + array.set(0, "bar"); + array.append("baz"); + } + + @Test(expected=IllegalStateException.class) + public void require_that_index_conflicts_with_append() { + ConfigPayloadBuilder builder = new ConfigPayloadBuilder(); + ConfigPayloadBuilder.Array array = builder.getArray("foo"); + array.append("baz"); + array.set(0, "bar"); + } + + @Test + public void require_that_builder_can_be_created_from_payload() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("foo", "bar"); + Cursor obj = root.setObject("foorio"); + obj.setString("bar", "bam"); + Cursor obj2 = obj.setObject("bario"); + obj2.setString("bim", "bul"); + Cursor a2 = obj.setArray("blim"); + Cursor arrayobj = a2.addObject(); + arrayobj.setString("fim", "fam"); + Cursor arrayobj2 = a2.addObject(); + arrayobj2.setString("blim", "blam"); + Cursor a1 = root.setArray("arrio"); + a1.addString("himbio"); + + ConfigPayloadBuilder builder = new ConfigPayloadBuilder(new ConfigPayload(slime)); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ConfigPayload.fromBuilder(builder).serialize(baos, new JsonFormat(true)); + assertThat(baos.toString(), is("{\"foo\":\"bar\",\"foorio\":{\"bar\":\"bam\",\"bario\":{\"bim\":\"bul\"},\"blim\":[{\"fim\":\"fam\"},{\"blim\":\"blam\"}]},\"arrio\":[\"himbio\"]}")); + } + + @Test(expected=IllegalArgumentException.class) + public void require_that_values_are_verified_against_def() { + builderWithDef.setField("boolval", "true"); + assertThat(builderWithDef.warnings().size(), is(0)); + builderWithDef.setField("boolval", "invalid"); + //assertThat(builderWithDef.warnings().size(), is(1)); + } + + @Test(expected=IllegalArgumentException.class) + public void require_that_arrays_must_exist() { + builderWithDef.getArray("arraydoesnotexist"); + //assertThat(builderWithDef.warnings().size(), is(1)); + } + + @Test(expected=IllegalArgumentException.class) + public void require_that_structs_must_exist() { + builderWithDef.getObject("structdoesnotexist"); + //assertThat(builderWithDef.warnings().size(), is(1)); + } + + @Test(expected=IllegalArgumentException.class) + public void require_that_definition_is_passed_to_childstruct() { + ConfigPayloadBuilder nestedStruct = builderWithDef.getObject("mystruct"); + assertThat(builderWithDef.warnings().size(), is(0)); + nestedStruct.setField("doesnotexit", "foo"); + //assertThat(builderWithDef.warnings().size(), is(1)); + } + + @Test(expected=IllegalArgumentException.class) + public void require_that_definition_is_passed_to_childstruct_but_invalid_field_will_throw() { + ConfigPayloadBuilder nestedStruct = builderWithDef.getObject("mystruct"); + nestedStruct.setField("foofield", "invalid"); + //assertThat(builderWithDef.warnings().size(), is(2)); + } + + @Test + public void require_that_definition_is_passed_to_childarray() { + ConfigPayloadBuilder.Array nestedArray = builderWithDef.getArray("myarray"); + assertThat(builderWithDef.warnings().size(), is(0)); + nestedArray.append("1337"); + assertThat(builderWithDef.warnings().size(), is(0)); + } + + @Test(expected=IllegalArgumentException.class) + public void require_that_definition_is_passed_to_childarray_but_invalid_field_will_throw() { + ConfigPayloadBuilder.Array nestedArray = builderWithDef.getArray("myarray"); + nestedArray.append("invalid"); + //assertThat(builderWithDef.warnings().size(), is(1)); + } + + @Test + public void require_that_definition_is_passed_to_inner_array_with_append() { + ConfigPayloadBuilder.Array innerArray = builderWithDef.getArray("myinnerarray"); + assertThat(builderWithDef.warnings().size(), is(0)); + ConfigPayloadBuilder innerStruct = innerArray.append(); + assertNotNull(innerStruct.getConfigDefinition()); + assertThat(builderWithDef.warnings().size(), is(0)); + innerStruct.setField("foo", "1337"); + assertThat(builderWithDef.warnings().size(), is(0)); + } + + @Test(expected=IllegalArgumentException.class) + public void require_that_definition_is_passed_to_inner_array_with_append_but_invalid_field_will_throw() { + ConfigPayloadBuilder.Array innerArray = builderWithDef.getArray("myinnerarray"); + assertThat(builderWithDef.warnings().size(), is(0)); + ConfigPayloadBuilder innerStruct = innerArray.append(); + innerStruct.setField("foo", "invalid"); + //assertThat(builderWithDef.warnings().size(), is(1)); + } + + @Test + public void require_that_definition_is_passed_to_inner_array_with_index() { + ConfigPayloadBuilder.Array innerArray = builderWithDef.getArray("myinnerarray"); + assertThat(builderWithDef.warnings().size(), is(0)); + ConfigPayloadBuilder innerStruct = innerArray.set(1); + assertNotNull(innerStruct.getConfigDefinition()); + assertThat(builderWithDef.warnings().size(), is(0)); + innerStruct.setField("foo", "1337"); + assertThat(builderWithDef.warnings().size(), is(0)); + } + + @Test(expected=IllegalArgumentException.class) + public void require_that_definition_is_passed_to_inner_array_with_index_but_invalid_field_will_throw() { + ConfigPayloadBuilder.Array innerArray = builderWithDef.getArray("myinnerarray"); + assertThat(builderWithDef.warnings().size(), is(0)); + ConfigPayloadBuilder innerStruct = innerArray.set(1); + innerStruct.setField("foo", "invalid"); + //assertThat(builderWithDef.warnings().size(), is(1)); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/ConfigPayloadTest.java b/config/src/test/java/com/yahoo/vespa/config/ConfigPayloadTest.java new file mode 100644 index 00000000000..1e7b66fac38 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/ConfigPayloadTest.java @@ -0,0 +1,461 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import com.yahoo.foo.*; +import com.yahoo.config.codegen.DefParser; +import com.yahoo.config.codegen.InnerCNode; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.Slime; +import com.yahoo.text.StringUtilities; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringReader; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + +/** + * @author lulf 3 + * @since 5.1 + */ +public class ConfigPayloadTest { + + @Test + public void test_simple_builder() throws Exception { + SimpletypesConfig config = createSimpletypesConfig("stringval", "abcde"); + assertThat(config.stringval(), is("abcde")); + } + + @Test + public void require_that_arrays_are_built() throws Exception { + AppConfig config = createAppConfig("foo", "4", new String[] { "bar", "baz", "bim" }); + assertThat(config.message(), is("foo")); + assertThat(config.times(), is(4)); + assertThat(config.a(0).name(), is("bar")); + assertThat(config.a(1).name(), is("baz")); + assertThat(config.a(2).name(), is("bim")); + } + + @Test + public void test_int_leaf_legal() throws Exception { + SimpletypesConfig config = createSimpletypesConfig("intval", "0"); + assertThat(config.intval(), is(0)); + config = createSimpletypesConfig("intval", String.valueOf(Integer.MIN_VALUE)); + assertThat(config.intval(), is(Integer.MIN_VALUE)); + config = createSimpletypesConfig("intval", String.valueOf(Integer.MAX_VALUE)); + assertThat(config.intval(), is(Integer.MAX_VALUE)); + config = createSimpletypesConfig("intval", String.valueOf(10)); + assertThat(config.intval(), is(10)); + config = createSimpletypesConfig("intval", String.valueOf(-10)); + assertThat(config.intval(), is(-10)); + } + + @Test (expected = RuntimeException.class) + public void test_int_leaf_too_large() throws Exception { + createSimpletypesConfig("intval", String.valueOf(Integer.MAX_VALUE) + "00"); + } + + @Test (expected = RuntimeException.class) + public void test_int_leaf_too_large_neg() throws Exception { + createSimpletypesConfig("intval", String.valueOf(Integer.MIN_VALUE) + "00"); + } + + @Test(expected=RuntimeException.class) + public void test_int_leaf_illegal_string() throws Exception { + createSimpletypesConfig("intval", "illegal"); + } + + @Test(expected=RuntimeException.class) + public void test_int_leaf_illegal_string_suffix() throws Exception { + createSimpletypesConfig("intval", "123illegal"); + } + + @Test(expected=RuntimeException.class) + public void test_int_leaf_illegal_string_prefix() throws Exception { + createSimpletypesConfig("intval", "illegal123"); + } + + @Test + public void test_that_empty_is_empty() { + ConfigPayload payload = ConfigPayload.empty(); + assertTrue(payload.isEmpty()); + payload = ConfigPayload.fromString("{\"foo\":4}"); + assertFalse(payload.isEmpty()); + } + + + @Test + public void test_long_leaf() throws Exception { + SimpletypesConfig config = createSimpletypesConfig("longval", "0"); + assertThat(config.longval(), is(0l)); + config = createSimpletypesConfig("longval", String.valueOf(Long.MIN_VALUE)); + assertThat(config.longval(), is(Long.MIN_VALUE)); + config = createSimpletypesConfig("longval", String.valueOf(Long.MAX_VALUE)); + assertThat(config.longval(), is(Long.MAX_VALUE)); + config = createSimpletypesConfig("longval", String.valueOf(10)); + assertThat(config.longval(), is(10l)); + config = createSimpletypesConfig("longval", String.valueOf(-10)); + assertThat(config.longval(), is(-10l)); + } + + @Test(expected = RuntimeException.class) + public void test_long_leaf_illegal_string() throws Exception { + createSimpletypesConfig("longval", "illegal"); + } + + @Test (expected = RuntimeException.class) + public void test_long_leaf_too_large() throws Exception { + createSimpletypesConfig("longval", String.valueOf(Long.MAX_VALUE) + "00"); + } + + @Test (expected = RuntimeException.class) + public void test_long_leaf_too_large_neg() throws Exception { + createSimpletypesConfig("longval", String.valueOf(Long.MIN_VALUE) + "00"); + } + + @Test + public void test_double_leaf() throws Exception { + SimpletypesConfig config = createSimpletypesConfig("doubleval", "0"); + assertEquals(0.0, config.doubleval(), 0.01); + assertEquals(133.3, createSimpletypesConfig("doubleval", "133.3").doubleval(), 0.001); + config = createSimpletypesConfig("doubleval", String.valueOf(Double.MIN_VALUE)); + assertEquals(Double.MIN_VALUE, config.doubleval(), 0.0000001); + config = createSimpletypesConfig("doubleval", String.valueOf(Double.MAX_VALUE)); + assertEquals(Double.MAX_VALUE, config.doubleval(), 0.0000001); + } + + @Test + public void test_serializer() throws IOException { + ConfigPayload payload = ConfigPayload.fromInstance(new SimpletypesConfig(new SimpletypesConfig.Builder())); + assertThat(payload.toString(true), is("{\"boolval\":false,\"doubleval\":0.0,\"enumval\":\"VAL1\",\"intval\":0,\"longval\":0,\"stringval\":\"s\"}")); + } + + @Test(expected=RuntimeException.class) + public void test_double_leaf_illegal_string() throws Exception { + createSimpletypesConfig("doubleval", "illegal"); + } + + @Test + public void test_double_leaf_negative_infinity() throws Exception { + assertThat(createSimpletypesConfig("doubleval", "-Infinity").doubleval(), is(Double.NEGATIVE_INFINITY)); + assertThat(createSimpletypesConfig("doubleval", "Infinity").doubleval(), is(Double.POSITIVE_INFINITY)); + } + + @Test + public void test_enum_leaf() throws Exception { + assertThat(createSimpletypesConfig("enumval", "VAL1").enumval(), is(SimpletypesConfig.Enumval.Enum.VAL1)); + assertThat(createSimpletypesConfig("enumval", "VAL2").enumval(), is(SimpletypesConfig.Enumval.Enum.VAL2)); + } + + @Test(expected=RuntimeException.class) + public void test_enum_leaf_illegal_string() throws Exception { + createSimpletypesConfig("enumval", "ILLEGAL"); + } + + @Test + public void test_bool_leaf() throws Exception { + SimpletypesConfig config = createSimpletypesConfig("boolval", "true"); + assertThat(config.boolval(), is(true)); + config = createSimpletypesConfig("boolval", "false"); + assertThat(config.boolval(), is(false)); + config = createSimpletypesConfig("boolval", "TRUE"); + assertThat(config.boolval(), is(true)); + config = createSimpletypesConfig("boolval", "FALSE"); + assertThat(config.boolval(), is(false)); + } + + @Test// FIXME: (expected = RuntimeException.class) + public void test_bool_leaf_illegal() throws Exception { + createSimpletypesConfig("boolval", "illegal"); + } + + @Test + public void test_string_illegal_value() throws Exception { + // TODO: What do we consider illegal string values? + createSimpletypesConfig("stringval", "insert_illegal_value_please"); + } + + @Test + public void test_int_array() throws Exception { + // Normal behavior + ArraytypesConfig config = createArraytypesConfig("intarr", new String[] { "2", "3", "1", "-2", "5"}); + assertThat(config.intarr().size(), is(5)); + assertThat(config.intarr(0), is(2)); + assertThat(config.intarr(1), is(3)); + assertThat(config.intarr(2), is(1)); + assertThat(config.intarr(3), is(-2)); + assertThat(config.intarr(4), is(5)); + + final int size = 100; + String [] largeArray = new String[size]; + for (int i = 0; i < size; i++) { + int value = (int)(Math.random() * Integer.MAX_VALUE); + largeArray[i] = String.valueOf(value); + } + config = createArraytypesConfig("intarr", largeArray); + assertThat(config.intarr().size(), is(largeArray.length)); + for (int i = 0; i < size; i++) { + assertThat(config.intarr(i), is(Integer.valueOf(largeArray[i]))); + } + } + + @Test(expected = RuntimeException.class) + public void test_int_array_illegal() throws Exception { + createArraytypesConfig("intarr", new String[] { "2", "3", "illegal", "-2", "5"}); + } + + @Test + public void test_long_array() throws Exception { + // Normal behavior + ArraytypesConfig config = createArraytypesConfig("longarr", new String[] { "2", "3", "1", "-2", "5"}); + assertThat(config.longarr().size(), is(5)); + assertThat(config.longarr(0), is(2l)); + assertThat(config.longarr(1), is(3l)); + assertThat(config.longarr(2), is(1l)); + assertThat(config.longarr(3), is(-2l)); + assertThat(config.longarr(4), is(5l)); + + final int size = 100; + String [] largeArray = new String[size]; + for (int i = 0; i < size; i++) { + long value = (long) (Math.random() * Long.MAX_VALUE); + largeArray[i] = String.valueOf(value); + } + config = createArraytypesConfig("longarr", largeArray); + assertThat(config.longarr().size(), is(largeArray.length)); + for (int i = 0; i < size; i++) { + assertThat(config.longarr(i), is(Long.valueOf(largeArray[i]))); + } + } + + @Test + public void test_double_array() throws Exception { + // Normal behavior + ArraytypesConfig config = createArraytypesConfig("doublearr", new String[] { "2.1", "3.3", "1.5", "-2.1", "Infinity"}); + assertThat(config.doublearr().size(), is(5)); + assertEquals(2.1, config.doublearr(0), 0.01); + assertEquals(3.3, config.doublearr(1), 0.01); + assertEquals(1.5, config.doublearr(2), 0.01); + assertEquals(-2.1, config.doublearr(3), 0.01); + assertEquals(Double.POSITIVE_INFINITY, config.doublearr(4), 0.01); + } + + @Test + public void test_enum_array() throws Exception { + // Normal behavior + ArraytypesConfig config = createArraytypesConfig("enumarr", new String[] { "VAL1", "VAL2", "VAL1" }); + assertThat(config.enumarr().size(), is(3)); + assertThat(config.enumarr(0), is(ArraytypesConfig.Enumarr.Enum.VAL1)); + assertThat(config.enumarr(1), is(ArraytypesConfig.Enumarr.Enum.VAL2)); + assertThat(config.enumarr(2), is(ArraytypesConfig.Enumarr.Enum.VAL1)); + } + + @Test + public void test_simple_struct() throws Exception { + StructtypesConfig config = createStructtypesConfigSimple("foobar", "MALE", new String[] { "foo@bar", "bar@foo" }); + assertThat(config.simple().name(), is("foobar")); + assertThat(config.simple().gender(), is(StructtypesConfig.Simple.Gender.Enum.MALE)); + assertThat(config.simple().emails(0), is("foo@bar")); + assertThat(config.simple().emails(1), is("bar@foo")); + } + + + @Test + public void test_simple_struct_arrays() throws Exception { + StructtypesConfig config = createStructtypesConfigArray(new String[] { "foo", "bar" }, + new String[] { "MALE", "FEMALE" }); + assertThat(config.simplearr(0).name(), is("foo")); + assertThat(config.simplearr(0).gender(), is(StructtypesConfig.Simplearr.Gender.MALE)); + assertThat(config.simplearr(1).name(), is("bar")); + assertThat(config.simplearr(1).gender(), is(StructtypesConfig.Simplearr.Gender.FEMALE)); + } + + + @Test + public void test_nested_struct() throws Exception { + StructtypesConfig config = createStructtypesConfigNested("foo", "FEMALE"); + assertThat(config.nested().inner().name(), is("foo")); + assertThat(config.nested().inner().gender(), is(StructtypesConfig.Nested.Inner.Gender.Enum.FEMALE)); + } + + + + @Test + public void test_nested_struct_array() throws Exception { + String [] names = { "foo" ,"bar" }; + String [] genders = { "FEMALE", "MALE" }; + String [][] emails = { + { "foo@bar" , "bar@foo" }, + { "bim@bam", "bam@bim" } + }; + StructtypesConfig config = createStructtypesConfigNestedArray(names, genders, emails); + assertThat(config.nestedarr(0).inner().name(), is("foo")); + assertThat(config.nestedarr(0).inner().gender(), is(StructtypesConfig.Nestedarr.Inner.Gender.FEMALE)); + assertThat(config.nestedarr(0).inner().emails(0), is("foo@bar")); + assertThat(config.nestedarr(0).inner().emails(1), is("bar@foo")); + + assertThat(config.nestedarr(1).inner().name(), is("bar")); + assertThat(config.nestedarr(1).inner().gender(), is(StructtypesConfig.Nestedarr.Inner.Gender.MALE)); + assertThat(config.nestedarr(1).inner().emails(0), is("bim@bam")); + assertThat(config.nestedarr(1).inner().emails(1), is("bam@bim")); + } + + + @Test + public void test_complex_struct_array() throws Exception { + String [][] names = { + { "foo", "bar" }, + { "baz", "bim" } + }; + String [][] genders = { + { "FEMALE", "MALE" }, + { "MALE", "FEMALE" } + }; + StructtypesConfig config = createStructtypesConfigComplexArray(names, genders); + assertThat(config.complexarr(0).innerarr(0).name(), is("foo")); + assertThat(config.complexarr(0).innerarr(0).gender(), is(StructtypesConfig.Complexarr.Innerarr.Gender.Enum.FEMALE)); + assertThat(config.complexarr(0).innerarr(1).name(), is("bar")); + assertThat(config.complexarr(0).innerarr(1).gender(), is(StructtypesConfig.Complexarr.Innerarr.Gender.Enum.MALE)); + + assertThat(config.complexarr(1).innerarr(0).name(), is("baz")); + assertThat(config.complexarr(1).innerarr(0).gender(), is(StructtypesConfig.Complexarr.Innerarr.Gender.Enum.MALE)); + assertThat(config.complexarr(1).innerarr(1).name(), is("bim")); + assertThat(config.complexarr(1).innerarr(1).gender(), is(StructtypesConfig.Complexarr.Innerarr.Gender.Enum.FEMALE)); + } + + @Test + public void test_function_test() { + // TODO: Test function test config as a complete config example + } + + @Test + public void test_set_nonexistent_field() throws Exception { + createSimpletypesConfig("doesnotexist", "blabla"); + } + + @Test + public void test_escaped_string() throws Exception { + SimpletypesConfig config = createSimpletypesConfig("stringval", "b=\"escaped\""); + assertThat(config.stringval(), is("b=\"escaped\"")); + } + + @Test + public void test_unicode() throws Exception { + SimpletypesConfig config = createSimpletypesConfig("stringval", "Hei \u00E6\u00F8\u00E5 \uBC14\uB451 \u00C6\u00D8\u00C5 hallo"); + assertThat(config.stringval(), is("Hei \u00E6\u00F8\u00E5 \uBC14\uB451 \u00C6\u00D8\u00C5 hallo")); + } + + @Test + public void test_empty_payload() throws Exception { + Slime slime = new Slime(); + slime.setObject(); + IntConfig config = new ConfigPayload(slime).toInstance(IntConfig.class, ""); + assertThat(config.intVal(), is(1)); + } + + @Test + public void test_applying_extra_default_values() { + InnerCNode clientDef = new DefParser(SimpletypesConfig.CONFIG_DEF_NAME, new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n") + "\nnewfield int default=3\n")).getTree(); + ConfigPayload payload = ConfigPayload.fromInstance(new SimpletypesConfig(new SimpletypesConfig.Builder())); + payload = payload.applyDefaultsFromDef(clientDef); + assertThat(payload.toString(true), is("{\"boolval\":false,\"doubleval\":0.0,\"enumval\":\"VAL1\",\"intval\":0,\"longval\":0,\"stringval\":\"s\",\"newfield\":\"3\"}")); + } + + /** + * TODO: Test invalid slime trees? + * TODO: Test sending in wrong class + */ + + /********************************************************************************************** + * Helper methods. consider moving out to another class for reuse by merge tester. * + **********************************************************************************************/ + private AppConfig createAppConfig(String message, String times, String [] names) { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("message", message); + root.setString("times", times); + Cursor arr = root.setArray("a"); + for (String name : names) { + Cursor obj = arr.addObject(); + obj.setString("name", name); + } + return new ConfigPayload(slime).toInstance(AppConfig.class, ""); + } + + private SimpletypesConfig createSimpletypesConfig(String field, String value) { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString(field, value); + return new ConfigPayload(slime).toInstance(SimpletypesConfig.class, ""); + } + + private ArraytypesConfig createArraytypesConfig(String field, String [] values) { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + Cursor array = root.setArray(field); + for (String value : values) { + array.addString(value); + } + return new ConfigPayload(slime).toInstance(ArraytypesConfig.class, ""); + } + + + private void addStructFields(Cursor struct, String name, String gender, String [] emails) { + struct.setString("name", name); + struct.setString("gender", gender); + if (emails != null) { + Cursor array = struct.setArray("emails"); + for (String email : emails) { + array.addString(email); + } + } + } + + private StructtypesConfig createStructtypesConfigSimple(String name, String gender, String [] emails) { + Slime slime = new Slime(); + addStructFields(slime.setObject().setObject("simple"), name, gender, emails); + return new ConfigPayload(slime).toInstance(StructtypesConfig.class, ""); + } + + private StructtypesConfig createStructtypesConfigArray(String[] names, String[] genders) { + Slime slime = new Slime(); + Cursor array = slime.setObject().setArray("simplearr"); + assertEquals(names.length, genders.length); + for (int i = 0; i < names.length; i++) { + addStructFields(array.addObject(), names[i], genders[i], null); + } + return new ConfigPayload(slime).toInstance(StructtypesConfig.class, ""); + } + + private StructtypesConfig createStructtypesConfigNested(String name, String gender) { + Slime slime = new Slime(); + addStructFields(slime.setObject().setObject("nested").setObject("inner"), name, gender, null); + return new ConfigPayload(slime).toInstance(StructtypesConfig.class, ""); + } + + private StructtypesConfig createStructtypesConfigNestedArray(String[] names, String [] genders, String [][] emails) { + Slime slime = new Slime(); + Cursor array = slime.setObject().setArray("nestedarr"); + assertEquals(names.length, genders.length); + for (int i = 0; i < names.length; i++) { + addStructFields(array.addObject().setObject("inner"), names[i], genders[i], emails[i]); + } + return new ConfigPayload(slime).toInstance(StructtypesConfig.class, ""); + } + + private StructtypesConfig createStructtypesConfigComplexArray(String [][] names, String [][] genders) { + Slime slime = new Slime(); + Cursor array = slime.setObject().setArray("complexarr"); + assertEquals(names.length, genders.length); + for (int i = 0; i < names.length; i++) { + assertEquals(names[i].length, genders[i].length); + + Cursor innerarr = array.addObject().setArray("innerarr"); + for (int k = 0; k < names[i].length; k++) { + addStructFields(innerarr.addObject(), names[i][k], genders[i][k], null); + } + } + return new ConfigPayload(slime).toInstance(StructtypesConfig.class, ""); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/DefaultValueApplierTest.java b/config/src/test/java/com/yahoo/vespa/config/DefaultValueApplierTest.java new file mode 100644 index 00000000000..623d81993d9 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/DefaultValueApplierTest.java @@ -0,0 +1,100 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import com.yahoo.config.codegen.DefParser; +import com.yahoo.config.codegen.InnerCNode; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.Slime; +import com.yahoo.slime.Type; +import org.junit.Test; + +import java.io.StringReader; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author lulf + * @since 5.1 + */ +public class DefaultValueApplierTest { + public Slime apply(Slime slime, String ... extraFields) { + StringBuilder defBuilder = new StringBuilder(); + defBuilder.append("namespace=test").append("\n"); + defBuilder.append("str string").append("\n"); + for (String field : extraFields) { + defBuilder.append(field).append("\n"); + } + Cursor cursor = slime.get(); + cursor.setString("str", "myvalue"); + InnerCNode def = new DefParser("simpletypes", new StringReader(defBuilder.toString())).getTree(); + DefaultValueApplier applier = new DefaultValueApplier(); + return applier.applyDefaults(slime, def); + } + + public Slime apply(String ... extraFields) { + Slime slime = new Slime(); + slime.setObject(); + return apply(slime, extraFields); + } + + @Test + public void require_that_simple_defaults_are_applied() { + Slime slime = apply("strdef string default=\"foo\""); + assertTrue(slime.get().field("str").valid()); + assertThat(slime.get().field("str").asString(), is("myvalue")); + assertTrue(slime.get().field("strdef").valid()); + assertThat(slime.get().field("strdef").asString(), is("foo")); + + } + + @Test + public void require_that_struct_fields_defaults_are_applied() { + Slime slime = apply("nested.str string default=\"bar\""); + assertTrue(slime.get().field("nested").valid()); + assertTrue(slime.get().field("nested").field("str").valid()); + assertThat(slime.get().field("nested").field("str").asString(), is("bar")); + } + + @Test + public void require_that_arrays_of_struct_fields_defaults_are_applied() { + Slime payload = new Slime(); + Cursor cursor = payload.setObject(); + cursor.setArray("nestedarr").addObject().setString("foo", "myfoo"); + Slime slime = apply(payload, "nestedarr[].foo string", "nestedarr[].bar string default=\"bim\""); + + assertTrue(slime.get().field("nestedarr").valid()); + assertThat(slime.get().field("nestedarr").entries(), is(1)); + assertTrue(slime.get().field("nestedarr").entry(0).field("foo").valid()); + assertThat(slime.get().field("nestedarr").entry(0).field("foo").asString(), is("myfoo")); + assertTrue(slime.get().field("nestedarr").entry(0).field("bar").valid()); + assertThat(slime.get().field("nestedarr").entry(0).field("bar").asString(), is("bim")); + } + + @Test + public void require_that_arrays_of_struct_fields_defaults_when_empty() { + Slime payload = new Slime(); + payload.setObject(); + Slime slime = apply(payload, "nestedarr[].foo string", "nestedarr[].bar string default=\"bim\""); + + assertTrue(slime.get().field("nestedarr").valid()); + assertThat(slime.get().field("nestedarr").entries(), is(0)); + assertThat(slime.get().field("nestedarr").type(), is(Type.ARRAY)); + } + + @Test + public void require_that_maps_of_struct_fields_defaults_are_applied() { + Slime payload = new Slime(); + Cursor cursor = payload.setObject(); + cursor.setObject("nestedmap").setObject("mykey").setString("foo", "myfoo"); + Slime slime = apply(payload, "nestedmap{}.foo string", "nestedmap{}.bar string default=\"bim\""); + + assertTrue(slime.get().field("nestedmap").valid()); + assertThat(slime.get().field("nestedmap").fields(), is(1)); + assertTrue(slime.get().field("nestedmap").field("mykey").field("foo").valid()); + assertThat(slime.get().field("nestedmap").field("mykey").field("foo").asString(), is("myfoo")); + assertTrue(slime.get().field("nestedmap").field("mykey").field("bar").valid()); + assertThat(slime.get().field("nestedmap").field("mykey").field("bar").asString(), is("bim")); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/ErrorCodeTest.java b/config/src/test/java/com/yahoo/vespa/config/ErrorCodeTest.java new file mode 100644 index 00000000000..249a5ab6123 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/ErrorCodeTest.java @@ -0,0 +1,35 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @since 5.1.9 + */ +public class ErrorCodeTest { + @Test + public void basic() { + assertThat(ErrorCode.getName(ErrorCode.INTERNAL_ERROR), is("INTERNAL_ERROR")); + assertThat(ErrorCode.getName(ErrorCode.ILLEGAL_CONFIG_MD5), is("ILLEGAL_CONFIG_MD5")); + assertThat(ErrorCode.getName(ErrorCode.ILLEGAL_CONFIGID), is("ILLEGAL_CONFIGID")); + assertThat(ErrorCode.getName(ErrorCode.ILLEGAL_DEF_MD5), is("ILLEGAL_DEF_MD5")); + assertThat(ErrorCode.getName(ErrorCode.ILLEGAL_GENERATION), is("ILLEGAL_GENERATION")); + assertThat(ErrorCode.getName(ErrorCode.ILLEGAL_NAME), is("ILLEGAL_NAME")); + assertThat(ErrorCode.getName(ErrorCode.ILLEGAL_SUB_FLAG), is("ILLEGAL_SUBSCRIBE_FLAG")); + assertThat(ErrorCode.getName(ErrorCode.ILLEGAL_TIMEOUT), is("ILLEGAL_TIMEOUT")); + assertThat(ErrorCode.getName(ErrorCode.ILLEGAL_VERSION), is("ILLEGAL_VERSION")); + assertThat(ErrorCode.getName(ErrorCode.OUTDATED_CONFIG), is("OUTDATED_CONFIG")); + assertThat(ErrorCode.getName(ErrorCode.UNKNOWN_CONFIG), is("UNKNOWN_CONFIG")); + assertThat(ErrorCode.getName(ErrorCode.UNKNOWN_DEF_MD5), is("UNKNOWN_DEF_MD5")); + assertThat(ErrorCode.getName(ErrorCode.UNKNOWN_DEFINITION), is("UNKNOWN_DEFINITION")); + assertThat(ErrorCode.getName(ErrorCode.UNKNOWN_VESPA_VERSION), is("UNKNOWN_VESPA_VERSION")); + assertThat(ErrorCode.getName(ErrorCode.INCONSISTENT_CONFIG_MD5), is("INCONSISTENT_CONFIG_MD5")); + assertThat(ErrorCode.getName(ErrorCode.ILLEGAL_CLIENT_HOSTNAME), is("ILLEGAL_CLIENT_HOSTNAME")); + + assertThat(ErrorCode.getName(12345), is("Unknown error")); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/ErrorTypeTest.java b/config/src/test/java/com/yahoo/vespa/config/ErrorTypeTest.java new file mode 100644 index 00000000000..d8af6584eca --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/ErrorTypeTest.java @@ -0,0 +1,35 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +/** + * @author lulf + * @since 5.1 + */ +public class ErrorTypeTest { + + @Test + public void testErrorType() { + assertThat(ErrorType.getErrorType(com.yahoo.jrt.ErrorCode.CONNECTION), is(ErrorType.TRANSIENT)); + assertThat(ErrorType.getErrorType(com.yahoo.jrt.ErrorCode.TIMEOUT), is(ErrorType.TRANSIENT)); + assertThat(ErrorType.getErrorType(ErrorCode.UNKNOWN_CONFIG), is(ErrorType.FATAL)); + assertThat(ErrorType.getErrorType(ErrorCode.UNKNOWN_DEFINITION), is(ErrorType.FATAL)); + assertThat(ErrorType.getErrorType(ErrorCode.UNKNOWN_DEF_MD5), is(ErrorType.FATAL)); + assertThat(ErrorType.getErrorType(ErrorCode.ILLEGAL_NAME), is(ErrorType.FATAL)); + assertThat(ErrorType.getErrorType(ErrorCode.ILLEGAL_VERSION), is(ErrorType.FATAL)); + assertThat(ErrorType.getErrorType(ErrorCode.ILLEGAL_CONFIGID), is(ErrorType.FATAL)); + assertThat(ErrorType.getErrorType(ErrorCode.ILLEGAL_DEF_MD5), is(ErrorType.FATAL)); + assertThat(ErrorType.getErrorType(ErrorCode.ILLEGAL_CONFIG_MD5), is(ErrorType.FATAL)); + assertThat(ErrorType.getErrorType(ErrorCode.ILLEGAL_TIMEOUT), is(ErrorType.FATAL)); + assertThat(ErrorType.getErrorType(ErrorCode.ILLEGAL_GENERATION), is(ErrorType.FATAL)); + assertThat(ErrorType.getErrorType(ErrorCode.ILLEGAL_SUB_FLAG), is(ErrorType.FATAL)); + assertThat(ErrorType.getErrorType(ErrorCode.OUTDATED_CONFIG), is(ErrorType.FATAL)); + assertThat(ErrorType.getErrorType(ErrorCode.INTERNAL_ERROR), is(ErrorType.FATAL)); + assertThat(ErrorType.getErrorType(ErrorCode.ILLEGAL_SUB_FLAG), is(ErrorType.FATAL)); + assertThat(ErrorType.getErrorType(0xdeadc0de), is(ErrorType.FATAL)); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/GenericConfigBuilderTest.java b/config/src/test/java/com/yahoo/vespa/config/GenericConfigBuilderTest.java new file mode 100644 index 00000000000..f745c70fe1b --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/GenericConfigBuilderTest.java @@ -0,0 +1,48 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import com.yahoo.config.subscription.ConfigInstanceUtil; +import com.yahoo.slime.JsonFormat; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +/** + * @author lulf + * @since 5.1 + */ +public class GenericConfigBuilderTest { + @Test + public void require_that_builder_can_be_overridden() throws IOException { + ConfigPayloadBuilder ba = new ConfigPayloadBuilder(); + ba.setField("foo", "bar"); + ConfigPayloadBuilder bb = new ConfigPayloadBuilder(); + bb.setField("foo", "baz"); + ConfigPayloadBuilder bc = new ConfigPayloadBuilder(); + bc.setField("foo", "bim"); + GenericConfig.GenericConfigBuilder a = new GenericConfig.GenericConfigBuilder(null, ba); + GenericConfig.GenericConfigBuilder b = new GenericConfig.GenericConfigBuilder(null, bb); + GenericConfig.GenericConfigBuilder c = new GenericConfig.GenericConfigBuilder(null, bc); + assertThat(getString(a), is("{\"foo\":\"bar\"}")); + assertThat(getString(b), is("{\"foo\":\"baz\"}")); + assertThat(getString(c), is("{\"foo\":\"bim\"}")); + ConfigInstanceUtil.setValues(a, b); + assertThat(getString(a), is("{\"foo\":\"baz\"}")); + assertThat(getString(b), is("{\"foo\":\"baz\"}")); + assertThat(getString(c), is("{\"foo\":\"bim\"}")); + ConfigInstanceUtil.setValues(c, a); + assertThat(getString(a), is("{\"foo\":\"baz\"}")); + assertThat(getString(b), is("{\"foo\":\"baz\"}")); + assertThat(getString(c), is("{\"foo\":\"baz\"}")); + } + + private String getString(GenericConfig.GenericConfigBuilder builder) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + builder.getPayload().serialize(baos, new JsonFormat(true)); + return baos.toString(); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/JRTConnectionPoolTest.java b/config/src/test/java/com/yahoo/vespa/config/JRTConnectionPoolTest.java new file mode 100644 index 00000000000..438d8edb430 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/JRTConnectionPoolTest.java @@ -0,0 +1,124 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import com.yahoo.config.subscription.ConfigSourceSet; +import org.junit.Test; + +import java.util.*; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.*; + +/** + * Tests for the JRTConnectionPool class. + * + * @author <a href="mailto:gunnarga@yahoo-inc.com">Gunnar Gauslaa Bergem</a> + * @author musum + */ +public class JRTConnectionPoolTest { + private static final List<String> sources = new ArrayList<>((Arrays.asList("host0", "host1", "host2"))); + + /** + * Tests that hash-based selection through the list works. + */ + @Test + public void test_random_selection_of_sourceBasicHashBasedSelection() { + JRTConnectionPool sourcePool = new JRTConnectionPool(sources); + assertThat(sourcePool.toString(), is("Address: host0\nAddress: host1\nAddress: host2\n")); + + Map<String, Integer> sourceOccurrences = new HashMap<>(); + for (int i = 0; i < 1000; i++) { + final String address = sourcePool.setNewCurrentConnection().getAddress(); + if (sourceOccurrences.containsKey(address)) { + sourceOccurrences.put(address, sourceOccurrences.get(address) + 1); + } else { + sourceOccurrences.put(address, 1); + } + } + for (int i = 0; i < sourcePool.getSize(); i++) { + assertTrue(sourceOccurrences.get(sourcePool.getSources().get(i).getAddress()) > 200); + } + } + + /** + * Tests that when there are two sources and several clients + * the sources will be chosen with about the same probability. + */ + @Test + public void testManySources() { + Map<String, Integer> timesUsed = new LinkedHashMap<>(); + + List<String> twoSources = new ArrayList<>(); + + twoSources.add("host0"); + twoSources.add("host1"); + JRTConnectionPool sourcePool = new JRTConnectionPool(twoSources); + + int count = 1000; + for (int i = 0; i < count; i++) { + String address = sourcePool.setNewCurrentConnection().getAddress(); + if (timesUsed.containsKey(address)) { + int times = timesUsed.get(address); + timesUsed.put(address, times + 1); + } else { + timesUsed.put(address, 1); + } + } + assertConnectionDistributionIsFair(timesUsed); + } + + // Tests that the number of times each connection is used is close to equal + private void assertConnectionDistributionIsFair(Map<String, Integer> connectionsUsedPerHost) { + double devianceDueToRandomSourceSelection = 0.13; + final int size = 1000; + int minHostCount = (int) (size/2 * (1 - devianceDueToRandomSourceSelection)); + int maxHostCount = (int) (size/2 * (1 + devianceDueToRandomSourceSelection)); + + for (Map.Entry<String, Integer> entry : connectionsUsedPerHost.entrySet()) { + Integer timesUsed = entry.getValue(); + assertTrue("Host 0 used " + timesUsed + " times, expected to be < " + maxHostCount, timesUsed < maxHostCount); + assertTrue("Host 0 used " + timesUsed + " times, expected to be > " + minHostCount, timesUsed > minHostCount); + } + } + + /** + * Tests that updating config sources works. + */ + @Test + public void updateSources() { + List<String> twoSources = new ArrayList<>(); + + twoSources.add("host0"); + twoSources.add("host1"); + JRTConnectionPool sourcePool = new JRTConnectionPool(twoSources); + + ConfigSourceSet sourcesBefore = sourcePool.getSourceSet(); + + // Update to the same set, should be equal + sourcePool.updateSources(twoSources); + assertThat(sourcesBefore, is(sourcePool.getSourceSet())); + + // Update to new set + List<String> newSources = new ArrayList<>(); + newSources.add("host2"); + newSources.add("host3"); + sourcePool.updateSources(newSources); + ConfigSourceSet newSourceSet = sourcePool.getSourceSet(); + assertNotNull(newSourceSet); + assertThat(newSourceSet.getSources().size(), is(2)); + assertThat(newSourceSet, is(not(sourcesBefore))); + assertTrue(newSourceSet.getSources().contains("host2")); + assertTrue(newSourceSet.getSources().contains("host3")); + + // Update to new set with just one host + List<String> newSources2 = new ArrayList<>(); + newSources2.add("host4"); + sourcePool.updateSources(newSources2); + ConfigSourceSet newSourceSet2 = sourcePool.getSourceSet(); + assertNotNull(newSourceSet2); + assertThat(newSourceSet2.getSources().size(), is(1)); + assertThat(newSourceSet2, is(not(newSourceSet))); + assertTrue(newSourceSet2.getSources().contains("host4")); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/LZ4CompressionTest.java b/config/src/test/java/com/yahoo/vespa/config/LZ4CompressionTest.java new file mode 100644 index 00000000000..5fb7c7a9a97 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/LZ4CompressionTest.java @@ -0,0 +1,74 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import net.jpountz.lz4.LZ4Compressor; +import net.jpountz.lz4.LZ4Factory; +import net.jpountz.lz4.LZ4SafeDecompressor; +import org.junit.Ignore; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Files; + +/** + * To run this test, place a payload in src/test/ca.json. The file is not checked in because it is huge. + * + * @author lulf + * @since 5.12 + */ +public class LZ4CompressionTest { + private static LZ4Factory factory = LZ4Factory.safeInstance(); + + @Test + @Ignore + public void testCompression() throws IOException { + byte[] data = getInput(); + System.out.println("High compressor"); + for (int i = 0; i < 10; i++) { + timeCompressor(factory.highCompressor(), data); + } + System.out.println("Fast compressor"); + for (int i = 0; i < 10; i++) { + timeCompressor(factory.fastCompressor(), data); + } + } + + private byte[] getInput() throws IOException { + byte[] data = Files.readAllBytes(FileSystems.getDefault().getPath("src/test/ca.json")); + System.out.println("Input size: " + data.length); + return data; + } + + private void timeCompressor(LZ4Compressor lz4Compressor, byte[] data) { + long start = System.currentTimeMillis(); + byte[] compressed = lz4Compressor.compress(data); + long end = System.currentTimeMillis(); + System.out.println("Compression took " + (end - start) + " millis, and size of data is " + compressed.length + " bytes"); + } + + @Test + @Ignore + public void testDecompression() throws IOException { + byte[] data = getInput(); + byte[] outputbuffer = new byte[data.length]; + byte[] hcCompressedData = factory.highCompressor().compress(data); + System.out.println("High compressor"); + for (int i = 0; i < 10; i++) { + timeDecompressor(hcCompressedData, factory.safeDecompressor(), outputbuffer); + } + byte[] fastCompressedData = factory.fastCompressor().compress(data); + System.out.println("Fast compressor"); + for (int i = 0; i < 10; i++) { + timeDecompressor(fastCompressedData, factory.safeDecompressor(), outputbuffer); + } + } + + private void timeDecompressor(byte[] compressedData, LZ4SafeDecompressor decompressor, byte[] outputbuffer) { + long start = System.currentTimeMillis(); + decompressor.decompress(compressedData, outputbuffer); + long end = System.currentTimeMillis(); + System.out.println("Decompression took " + (end - start) + " millis"); + } + +} diff --git a/config/src/test/java/com/yahoo/vespa/config/LZ4PayloadCompressorTest.java b/config/src/test/java/com/yahoo/vespa/config/LZ4PayloadCompressorTest.java new file mode 100644 index 00000000000..4b5e5ae07fe --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/LZ4PayloadCompressorTest.java @@ -0,0 +1,30 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import com.yahoo.text.Utf8; +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +/** + * @author lulf + * @since 5.19 + */ +public class LZ4PayloadCompressorTest { + @Test + public void testCompression() { + assertCompression("hei hallo der"); + assertCompression(""); + assertCompression("{}"); + } + + private void assertCompression(String input) { + LZ4PayloadCompressor compressor = new LZ4PayloadCompressor(); + byte[] data = Utf8.toBytes(input); + byte[] compressed = compressor.compress(data); + byte[] output = new byte[data.length]; + compressor.decompress(compressed, output); + assertThat(data, is(output)); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/RawConfigTest.java b/config/src/test/java/com/yahoo/vespa/config/RawConfigTest.java new file mode 100644 index 00000000000..7a316838334 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/RawConfigTest.java @@ -0,0 +1,123 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import com.yahoo.text.Utf8String; +import com.yahoo.vespa.config.protocol.*; +import com.yahoo.vespa.config.protocol.VespaVersion; +import com.yahoo.vespa.config.util.ConfigUtils; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNull; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertFalse; + +/** + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + * @since 5.1.9 + */ +public class RawConfigTest { + + private static final ConfigKey<?> key = new ConfigKey<>("foo", "id", "bar"); + private static List<String> defContent = Arrays.asList("version=1", "anInt int"); + private static final String defMd5 = ConfigUtils.getDefMd5FromRequest("", defContent); + private static final String configMd5 = "012345"; + private static Payload payload = Payload.from(new Utf8String("anInt 1"), CompressionInfo.uncompressed()); + private static long generation = 1L; + + @Test + public void basic() { + RawConfig config = new RawConfig(key, defMd5); + assertEquals(config.getKey(), key); + assertThat(config.getDefMd5(), is(defMd5)); + assertThat(config.getName(), is("foo")); + assertThat(config.getDefNamespace(), is("bar")); + assertThat(config.getConfigId(), is("id")); + + assertThat(config.isError(), is(false)); + + // Copy constructor + RawConfig copiedConfig = new RawConfig(config); + assertThat(copiedConfig, is(config)); + + assertThat(config.toString(), is("bar.foo," + defMd5 + ",id,,0,null")); + assertThat(config.getVespaVersion(), is(Optional.empty())); + } + + @Test + public void testEquals() { + RawConfig config = new RawConfig(key, defMd5); + + assertThat(config, is(new RawConfig(key, defMd5))); + assertThat(config, is(not(new RawConfig(key, "a")))); // different def md5 + assertThat(config.hashCode(), is(new RawConfig(key, defMd5).hashCode())); + assertThat(config.hashCode(), is(not(new RawConfig(key, "a").hashCode()))); // different def md5 + + // different generation + config = new RawConfig(key, defMd5, payload, configMd5, generation, defContent, Optional.empty()); + RawConfig config2 = new RawConfig(key, defMd5, payload, configMd5, 2L, defContent, Optional.empty()); + assertThat(config, is(not(config2))); + assertThat(config.hashCode(), is(not(config2.hashCode()))); + + // different config md5 and with vespa version + final VespaVersion vespaVersion = VespaVersion.fromString("5.37.38"); + RawConfig config3 = new RawConfig(key, defMd5, payload, "9999", generation, defContent, Optional.of(vespaVersion)); + assertThat(config, is(not(config3))); + assertThat(config.hashCode(), is(not(config3.hashCode()))); + // Check that vespa version is set correctly + assertThat(config3.getVespaVersion().get().toString(), is(vespaVersion.toString())); + assertThat(config.getVespaVersion(), is(not(config3.getVespaVersion()))); + + // null config + assertFalse(config.equals(null)); + + // different type of object + assertFalse(config.equals(key)); + + // errors + RawConfig errorConfig1 = new RawConfig(key, defMd5, payload, configMd5, generation, 1, defContent, Optional.empty()); + assertThat(errorConfig1, is(errorConfig1)); + assertThat(config, is(not(errorConfig1))); + assertThat(config.hashCode(), is(not(errorConfig1.hashCode()))); + assertThat(errorConfig1, is(errorConfig1)); + RawConfig errorConfig2 = new RawConfig(key, defMd5, payload, configMd5, generation, 2, defContent, Optional.empty()); + assertThat(errorConfig1, is(not(errorConfig2))); + assertThat(errorConfig1.hashCode(), is(not(errorConfig2.hashCode()))); + } + + @Test + public void payload() { + RawConfig config = new RawConfig(key, defMd5, payload, configMd5, generation, defContent, Optional.empty()); + assertThat(config.getPayload(), is(payload)); + assertThat(config.getConfigMd5(), is(configMd5)); + assertThat(config.getGeneration(), is(generation)); + assertThat(config.getDefContent(), is(defContent)); + } + + @Test + public void require_correct_defmd5() { + final String defMd5ForEmptyDefContent = "d41d8cd98f00b204e9800998ecf8427e"; + + RawConfig config = new RawConfig(key, null, payload, configMd5, generation, defContent, Optional.empty()); + assertThat(config.getDefMd5(), is(defMd5)); + config = new RawConfig(key, "", payload, configMd5, generation, defContent, Optional.empty()); + assertThat(config.getDefMd5(), is(defMd5)); + config = new RawConfig(key, defMd5, payload, configMd5, generation, defContent, Optional.empty()); + assertThat(config.getDefMd5(), is(defMd5)); + config = new RawConfig(key, null, payload, configMd5, generation, null, Optional.empty()); + assertNull(config.getDefMd5()); + config = new RawConfig(key, null, payload, configMd5, generation, Arrays.asList(""), Optional.empty()); + assertThat(config.getDefMd5(), is(defMd5ForEmptyDefContent)); + config = new RawConfig(key, "", payload, configMd5, generation, null, Optional.empty()); + assertThat(config.getDefMd5(), is("")); + config = new RawConfig(key, "", payload, configMd5, generation, Arrays.asList(""), Optional.empty()); + assertThat(config.getDefMd5(), is(defMd5ForEmptyDefContent)); + } + +} diff --git a/config/src/test/java/com/yahoo/vespa/config/RequestValidationTest.java b/config/src/test/java/com/yahoo/vespa/config/RequestValidationTest.java new file mode 100644 index 00000000000..1e237145b02 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/RequestValidationTest.java @@ -0,0 +1,35 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import com.yahoo.vespa.config.protocol.RequestValidation; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class RequestValidationTest { + + @Test + public void testVerifyName() { + assertTrue(RequestValidation.verifyName("foo")); + assertTrue(RequestValidation.verifyName("Foo")); + assertTrue(RequestValidation.verifyName("foo-bar")); + assertFalse(RequestValidation.verifyName("1foo")); + assertTrue(RequestValidation.verifyName("foo_bar")); + } + + @Test + public void testVerifyDefMd5() { + assertTrue(RequestValidation.verifyMd5("")); + assertTrue(RequestValidation.verifyMd5("e8f0c01c7c3dcb8d3f62d7ff777fce6b")); + assertTrue(RequestValidation.verifyMd5("e8f0c01c7c3dcb8d3f62d7ff777fce6B")); + assertFalse(RequestValidation.verifyMd5("aaaaaaaaaaaaaaaaaa")); + assertFalse(RequestValidation.verifyMd5("-8f0c01c7c3dcb8d3f62d7ff777fce6b")); + } + + @Test + public void testVerifyTimeout() { + assertTrue(RequestValidation.verifyTimeout(1000L)); + assertFalse(RequestValidation.verifyTimeout(-1000L)); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/SlimeUtilsTest.java b/config/src/test/java/com/yahoo/vespa/config/SlimeUtilsTest.java new file mode 100644 index 00000000000..742fed77c36 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/SlimeUtilsTest.java @@ -0,0 +1,82 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import com.yahoo.slime.Cursor; +import com.yahoo.slime.Slime; +import com.yahoo.text.Utf8; +import org.junit.Test; + +import java.io.IOException; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author lulf + * @since 5.8 + */ +public class SlimeUtilsTest { + @Test + public void test_copying_slime_types_into_cursor() { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("foo", "foobie"); + Cursor subobj = root.setObject("bar"); + + Slime slime2 = new Slime(); + Cursor root2 = slime2.setObject(); + root2.setString("a", "a"); + root2.setLong("b", 2); + root2.setBool("c", true); + root2.setDouble("d", 3.14); + root2.setData("e", new byte[]{0x64}); + root2.setNix("f"); + + SlimeUtils.copyObject(slime2.get(), subobj); + + assertThat(root.toString(), is("{\"foo\":\"foobie\",\"bar\":{\"a\":\"a\",\"b\":2,\"c\":true,\"d\":3.14,\"e\":\"0x64\",\"f\":null}}")); + } + + @Test + public void test_copying_slime_arrays_into_cursor() { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("foo", "foobie"); + Cursor subobj = root.setObject("bar"); + + Slime slime2 = new Slime(); + Cursor root2 = slime2.setObject(); + Cursor array = root2.setArray("a"); + array.addString("foo"); + array.addLong(4); + array.addBool(true); + array.addDouble(3.14); + array.addNix(); + array.addData(new byte[]{0x64}); + Cursor objinner = array.addObject(); + objinner.setString("inner", "binner"); + + SlimeUtils.copyObject(slime2.get(), subobj); + + assertThat(root.toString(), is("{\"foo\":\"foobie\",\"bar\":{\"a\":[\"foo\",4,true,3.14,null,\"0x64\",{\"inner\":\"binner\"}]}}")); + } + + @Test + public void test_slime_to_json() throws IOException { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("foo", "foobie"); + root.setObject("bar"); + String json = Utf8.toString(SlimeUtils.toJsonBytes(slime)); + assertThat(json, is("{\"foo\":\"foobie\",\"bar\":{}}")); + } + + @Test + public void test_json_to_slime() { + byte[] json = Utf8.toBytes("{\"foo\":\"foobie\",\"bar\":{}}"); + Slime slime = SlimeUtils.jsonToSlime(json); + assertThat(slime.get().field("foo").asString(), is("foobie")); + assertTrue(slime.get().field("bar").valid()); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/SourceTest.java b/config/src/test/java/com/yahoo/vespa/config/SourceTest.java new file mode 100644 index 00000000000..989131c8256 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/SourceTest.java @@ -0,0 +1,166 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import com.yahoo.config.ConfigurationRuntimeException; +import com.yahoo.foo.SimpletypesConfig; +import com.yahoo.vespa.config.protocol.CompressionType; +import com.yahoo.vespa.config.protocol.DefContent; +import com.yahoo.vespa.config.protocol.JRTClientConfigRequest; +import com.yahoo.vespa.config.protocol.JRTClientConfigRequestV3; +import com.yahoo.vespa.config.protocol.Payload; +import com.yahoo.vespa.config.protocol.Trace; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.*; + +/** + * @author lulf + * @since 5.1 + */ +public class SourceTest { + + @Test + public void testSourceInterface() { + MockSourceConfig config = new MockSourceConfig(new ConfigKey<>(SimpletypesConfig.class, "foobio")); + assertThat(config.getKey().getConfigId(), is("foobio")); + config.setConfig(JRTClientConfigRequestV3.createWithParams(config.getKey(), DefContent.fromList(config.getDefContent()), "host", config.getDefMd5(), config.getGeneration(), 1000, Trace.createNew(), CompressionType.LZ4, Optional.empty())); + MockSource src = new MockSource(config); + assertThat(src.getState(), is(Source.State.NEW)); + src.open(); + assertTrue(src.opened); + assertTrue(src.getconfigged); + assertThat(src.getState(), is(Source.State.OPEN_PENDING)); + src.open(); + assertThat(src.getState(), is(Source.State.OPEN_PENDING)); + src.getconfigged = false; + src.getConfig(); + assertTrue(src.getconfigged); + assertThat(src.getState(), is(Source.State.OPEN_PENDING)); + assertTrue(config.setConfigCalled); + assertTrue(src.openTimestamp > 0); + config.notifyInitMonitor(); + config.setGeneration(4); + src.cancel(); + assertTrue(src.canceled); + assertThat(src.getState(), is(Source.State.CANCEL_REQUESTED)); + src.setState(Source.State.CANCELLED); + try { + src.open(); + fail("Expected exception"); + } catch (ConfigurationRuntimeException e) { + } + src.getconfigged = false; + src.getConfig(); + assertFalse(src.getconfigged); + src.canceled = false; + src.cancel(); + assertFalse(src.canceled); + } + + public static class MockSource extends Source { + boolean opened, getconfigged, canceled = false; + + public MockSource(SourceConfig sourceConfig) { + super(sourceConfig); + } + + @Override + public void myOpen() { + opened = true; + } + + @Override + protected void myGetConfig() { + getconfigged = true; + } + + @Override + public void myCancel() { + canceled = true; + } + } + + private static class MockSourceConfig implements SourceConfig { + + boolean notifyCalled = false; + ConfigKey<?> key = null; + boolean setConfigCalled = false; + long generation = -1; + Payload payload; + + public MockSourceConfig(ConfigKey<?> key) { + this.key = key; + } + + @Override + public void notifyInitMonitor() { + notifyCalled = true; + } + + @Override + public void setConfig(com.yahoo.vespa.config.protocol.JRTClientConfigRequest req) { + key = req.getConfigKey(); + setConfigCalled = true; + } + + @Override + public void setGeneration(long generation) { + this.generation = generation; + } + + @Override + public String getDefName() { + return key.getName(); + } + + @Override + public String getDefNamespace() { + return key.getNamespace(); + } + + @Override + public String getDefVersion() { + return ""; + } + + @Override + public List<String> getDefContent() { + return Arrays.asList("foo"); + } + + @Override + public String getDefMd5() { + return key.getMd5(); + } + + @Override + public String getConfigId() { + return key.getConfigId(); + } + + @Override + public ConfigKey<?> getKey() { + return key; + } + + @Override + public String getConfigMd5() { + return "bar"; + } + + @Override + public long getGeneration() { + return 0; + } + + @Override + public RawConfig getConfig() { + return new RawConfig(getKey(), getDefMd5(), payload, getConfigMd5(), generation, getDefContent(), Optional.empty()); + } + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/TimingValuesTest.java b/config/src/test/java/com/yahoo/vespa/config/TimingValuesTest.java new file mode 100644 index 00000000000..1bae6c79ccb --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/TimingValuesTest.java @@ -0,0 +1,24 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +/** + * Note: Most of the functionality is tested implicitly by other tests + * + * @author <a href="mailto:musum@yahoo-inc.com">Harald Musum</a> + */ +public class TimingValuesTest { + @Test + public void basic() { + TimingValues tv = new TimingValues(); + TimingValues tv2 = new TimingValues(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1); + assertThat(tv.getRandom(), is(not(tv2.getRandom()))); + TimingValues copy = new TimingValues(tv2); + assertThat(copy.toString(), is(tv2.toString())); // No equals method, just using toString to compare + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/buildergen/ConfigBuilderGeneratorTest.java b/config/src/test/java/com/yahoo/vespa/config/buildergen/ConfigBuilderGeneratorTest.java new file mode 100644 index 00000000000..3d24372c40c --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/buildergen/ConfigBuilderGeneratorTest.java @@ -0,0 +1,51 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.buildergen; + +import com.google.common.io.Files; +import com.yahoo.config.ConfigInstance; +import com.yahoo.config.codegen.ConfigGenerator; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.Slime; +import com.yahoo.vespa.config.ConfigDefinitionKey; +import com.yahoo.vespa.config.ConfigPayload; +import com.yahoo.vespa.config.ConfigPayloadApplier; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URISyntaxException; + +import static junit.framework.TestCase.assertNotNull; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author lulf + * @since 5.1 + */ +public class ConfigBuilderGeneratorTest { + @Test + public void require_that_custom_classes_can_be_generated() throws URISyntaxException, IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { + String[] schema = new String[] { + "namespace=foo.bar", + "intval int", + "stringval string" + }; + File tempDir = Files.createTempDir(); + ConfigDefinitionKey key = new ConfigDefinitionKey("quux", "foo.bar"); + ConfigCompiler compiler = new LazyConfigCompiler(tempDir); + ConfigInstance.Builder builder = compiler.compile(new ConfigDefinition(key.getName(), schema).generateClass()).newInstance(); + assertNotNull(builder); + ConfigPayloadApplier<?> payloadApplier = new ConfigPayloadApplier<>(builder); + Slime slime = new Slime(); + Cursor root = slime.setObject(); + root.setString("intval", "3"); + root.setString("stringval", "Hello, world"); + payloadApplier.applyPayload(new ConfigPayload(slime)); + String className = ConfigGenerator.createClassName(key.getName()); + ConfigInstance instance = (ConfigInstance) builder.getClass().getClassLoader().loadClass("com.yahoo." + key.getNamespace() + "." + className).getConstructor(new Class<?>[]{builder.getClass()}).newInstance(builder); + assertNotNull(instance); + assertThat(instance.toString(), is("intval 3\nstringval \"Hello, world\"")); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/classes/app.1.def b/config/src/test/java/com/yahoo/vespa/config/classes/app.1.def new file mode 100644 index 00000000000..63e986b58ab --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/classes/app.1.def @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +version=1 + +message string default="Hello!" + +times int default=1 + +a[].name string diff --git a/config/src/test/java/com/yahoo/vespa/config/classes/qr-logging.def b/config/src/test/java/com/yahoo/vespa/config/classes/qr-logging.def new file mode 100644 index 00000000000..57ad0050a26 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/classes/qr-logging.def @@ -0,0 +1,34 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +version=3 +logger string default="com.yahoo" +# Either QueryAccessLog for a regular Vespa access log, or YApacheAccessLog for a log on yApache format +speciallog[].name string + +# Leave as "" +speciallog[].type string + +speciallog[].filehandler.name string default="" + +# File name patterns supporting the expected time variables +speciallog[].filehandler.pattern string default=".%Y%m%d%H%M%S" + +speciallog[].filehandler.rotation string default="0 60 ..." + +# Defines how file rotation is done. There are two options: +# +# "date" : +# The active log file is given the name resulting from pattern (but in this case "pattern" must yield a +# time-dependent name. In addition, a symlink is created pointing to the newest file. +# The symlink is given the name of the symlink parameter (or the name of this service +# if no parameter is given. +# +# "sequence" : +# The active log file is given the name +# defined by "pattern" (which in this case will likely just be a constant string). +# At rotation, this file is given the name pattern.N where N is 1 + the largest integer found by +# extracting the integers from all files ending by .Integer in the same directory +# +speciallog[].filehandler.rotatescheme string default="date" + +speciallog[].cachehandler.name string default="" +speciallog[].cachehandler.size int default=1000 diff --git a/config/src/test/java/com/yahoo/vespa/config/classes/qr-templates.3.def b/config/src/test/java/com/yahoo/vespa/config/classes/qr-templates.3.def new file mode 100644 index 00000000000..8483bf03c44 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/classes/qr-templates.3.def @@ -0,0 +1,142 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +version=3 + +## Directory for temporary files +directory string default="tmp/templates" + +hey[].ho[].lets[].go string default="ramones" +hey[].ho[].lets[].fishing int default=-4 range=[-8,0] +hey[].ho[].lets[].gone int default=200 range=[0,1000000] +hey[].ho[].lets[].ref reference +hey[].ho[].gone int default=2000 range=[-10,2000000] +hey[].ho[].going bool default=false +hey[].ho[].wash double default=345.3 +hey[].ho[].me double default=-45.5 range=[-234.43,0] +hey[].ho[].now double default=-34 range=[-234,0] +hi[].there[].e enum { BATCH, REALTIME, INCREMENTAL} default=BATCH +hi[].ther[].f enum { BATCH, REALTIME } default=BATCH + +#hey[] int +mode enum { BATCH, REALTIME, INCREMENTAL} default=BATCH +bar.arrline[] string +az[] double +bar.arline[] int range=[0,999] +#bar[].version int +b1[].b2[].b3[].b4[] bool +## Capacities for all storage nodes +capacity[] double range=[0,100] + +longVal long +longWithDefault long default=9876543210 +longWithRange long range=[-9000000000,0] +longArr[] long +longArrWithRange[] long range=[0,9000000000] + +fileVal file +fileWithDefault file +fileArr[] file + +washing double default=5 range=[-1.4,34.324432] +washer double default=46 range=[-1.6,54] + +urlprefix string + +## Prefix to use in queries to choose a given template +templateset[].urlprefix string + +## The MIME type of a given template +templateset[].mimetype string default="text/html" + +## The character set of a given template +templateset[].encoding string default="iso-8859-1" + +## Not used +templateset[].rankprofile int default=0 + + +## Not used in 1.0 +templateset[].keepalive bool default=false + +## Header template. Always rendered. +templateset[].headertemplate string + +## Footer template. Always rendered. +templateset[].footertemplate string + +## Nohits template. Rendered if there are no hits in the result. +templateset[].nohitstemplate string + +## Hit template. Rendered if there are hits in the result. +templateset[].hittemplate string + +## Error template. Rendered if there is an error condition. This is +## not mutually exclusive with the (no)hit templates as such. +templateset[].errortemplate string + +groupsheadertemplate string default="[DEFAULT]" + +## Aggregated groups header template. +## Default rendering is used if missing +templateset[].groupsheadertemplate string default="[DEFAULT]" + +## Aggregated range group template. +## Default rendering is used if missing +templateset[].rangegrouptemplate string default="[DEFAULT]" + +## Aggregated exact group template +## Default rendering is used if missing +templateset[].exactgrouptemplate string default="[DEFAULT]" + +## Aggregated groups footer template. +## Default rendering is used if missing +templateset[].groupsfootertemplate string default="[DEFAULT]" + +## Tags used to highlight results, starting a bolded section. +## An empty string means the template should no override what +## was inserted by the search chain. +templateset[].highlightstarttag string default="" +## Tags used to highlight results, ending a bolded section +## An empty string means the template should no override what +## was inserted by the search chain. +templateset[].highlightendtag string default="" +## Tags used to highlight results, separating dynamic snippets +## An empty string means the template should no override what +## was inserted by the search chain. +templateset[].highlightseptag string default="" + +## The summary class to use for this template if there is none +## defined in the query. +ilscript[].name string +ilscript[].doctype string +ilscript[].content[] string +config[].id reference +config[].autostart string default="no" +musum string + +auran string + +route[].name string +route[].selector string +route[].feed string + +languages[] string +languages2[] string +foolang[].lang[] string + +# Maps +myIntMap{} int +myStringMap{} string +myStructMap{}.myInt int +myStructMap{}.myString string +myStructMap{}.myIntDef int default=56 +myStructMap{}.myStringDef string default="g" + +myStructMap{}.myNestedLeafMap{} long +myStructMap{}.myNestedArray[] long + +myStructMap{}.myNestedMap{}.myLong long +myStructMap{}.myNestedMap{}.myLongDef long default=-100 + +myStructMap{}.myStruct.a string +myStructMap{}.myStruct.b string default="pizza" +myStructMap{}.myStruct.c file diff --git a/config/src/test/java/com/yahoo/vespa/config/classes/ranges.1.def b/config/src/test/java/com/yahoo/vespa/config/classes/ranges.1.def new file mode 100644 index 00000000000..bd054750db5 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/classes/ranges.1.def @@ -0,0 +1,5 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +version=1 +quux int default=5 range=[,] +xyzzy double default=5 range=[,] +longVal long default=5 range=[,] diff --git a/config/src/test/java/com/yahoo/vespa/config/classes/testfoobar.12.def b/config/src/test/java/com/yahoo/vespa/config/classes/testfoobar.12.def new file mode 100644 index 00000000000..11ac4e0b247 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/classes/testfoobar.12.def @@ -0,0 +1,925 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +version=12-1-2 + +longVal long +longWithDefault long default=8589934592 + +fileVal file +fileWithDefault file + +vh[] double range=[-300,300] +bg[] int default=0 range=[-10,10] +gee[] string +storage[].feeder[] string +storage[].distributor[] string + +ju[].hu[].tu[] double default=-45 range=[0,1000.1] +ju[].hu[].wu[] enum { HEY, HO} default=HO +ju[].hu[].tang[] bool default=false +ju[].hu[].clan[] int default=45 range=[-90,90] +ju[].hu[].sann reference + +foo string +headertemplate string +## If true, the bitvector part of results with number of hits within +## max bin size will be merged into array form before applying static +## rank. NB: Setting this to true may reduce performance. +applystaticranktosmallbitvectors bool default=true + +## Size of bitvector cache (in bytes). +bitvectorcachesize int default=50000000 + +## Size of boolean occurrence cache (in bytes). +boolocccachesize int default=100000000 + +## Size of dictionary cache (in bytes). +dictcachesize int default=40000000 + +onedimstruct[].val string + +## Size of document info cache (in bytes). +documentinfocachesize int default=25000000 + +## Size of filter occurrence cache (in bytes). +## This cache is used to optimizse exclusion handling. +## A too small size limit will cause serious performance degradation +## if the staticfilterindexes keyword is used. +filterocccachesize int default=30000000 + +## Size of integer range bitvector cache (in bytes). +intrangebitvectorcachesize int default=50000000 + +## Size of integer occurrence cache (in bytes). +intocccachesize int default=100000000 + +## Size of phrase occurrence cache (in bytes). +phrasecachesize int default=150000000 + +## Size of phrase occurrence index cache (in bytes). +phraseidxcachesize int default=20000000 + +## Size of position occurrence cache (in bytes). +posocccachesize int default=100000000 + +## The filename of a file specifying which hosts that should have +## access to the internal web-server of the fsearch process. +accesslist string default="" + +## The filename of the log file for HTTP access. +accesslog string default="" + +## The number of threads to perform async occurrence fetch, i.e. +## read from posocc and boolocc files and possibly generation of +## posocc/boolocc arrays for phrases. Async occurrence fetches +## will use more system CPU but can reduce latency on lightly +## loaded systems. +asyncfetchocc int default=0 + +## specifies the high limit of the ranked result bin. +## If the percentage of the resultset specified in binsize is higher +## than this limit, this will be the max size. +binhigh int default=2147483647 + +## specifies the lowest possible size of a ranked result. This is +## the lower ramp of the percentage specified in the binsize variable. +binlow int default=10000 + +## specifies the size of the ranked results as a percentage of the +## total result set size. The percentage can be ramped off with the +## binlow and binhigh variables. NB: Setting this to 100.0 may lead to +## seriously reduced performance. +binsize double default=100.0 + +## Check cache lines beyond offset + maxhits for blacklisting +## in order to provide correct "totalhits" for queries with many hits. +checktrailingcachelines bool default=false + +## specifies the number of result entries that are processed when +## doing site collapsing. Site collapsing is performed by reordering +## the first collapseentries hits in the result set, listing +## the best hit from each site first. +collapseentries int default=200 range=[0,1000000] + +## Specifies what is the default field used for collapse +defaultcollapse string default="" + +## specifies the rank penalty for additional hits from a site found +## during site collapsing. When additional hits from a site are +## found, the rank values of those hits are reduced by shifting +## them collapserankshift places to the right. +collapserankshift int default=0 + +## The maximum number of active and queued requests. Exceeding +## requests will be discarded. +cutofftransportconns int default=1024 + +## specifies the directory where the dataset resides. Only the +## directory needs to be specified. fsearch will then read the config +## files (index.cf) in that directory to discover the structure. +datasetdir string default="" + +## If set to "yes", then fallback to the default index if a query +## term specifies a non-existing index. If set to "no", then always +## return 0 hits for a query term that specifies a non-existing index. +## Note that the result set for the entire query might still contain +## hits from other query terms unless the invalid query term was an +## and-term. Default value is "no". +defaultindexfallback string default="no" + +# ??? +docattrhashsize int default=8171 +# ??? +docidhashsize int default=8171 +# ??? +docport int default=0 + +## If present, the result cache is not flushed due to reload operation +## unless the document summaries have changed. +dontflushresultcacheonreload bool default=false + +## Colon-delimeted list of catalogs for which negative dictionary +## entries should not be cached. Default is unset. Most useful for +## dictionary files that are memorymaped or memorylocked in +## indextune.cf. Note that the list must start and end with a colon (:). +dropnegativedictionarycacheentriescatalogs string default="" + +## specifies a term (used in an ANDNOT) to be used for filtering ALL queries. +excludefilter string default="" + +## If set to "yes", use of firstoccproximity is enabled. If set to +## "no", use of firstoccproximity is disabled. +firstoccproximity string default="" + +## If present, the filter occurrence cache is flushed due to reload +## operation when all queries using the old configuration has completed. +flushfilteroccscacheonreload bool default=false + +## Do not use position occurrence information, even though it might +## be present in the index. +forceemptyposoccs bool default=false + +## The port to run Fnet Remote Tools (RPC) service on. +## If set to 0, no FRT service is provided. +frtport int default=0 + +## The directory where gid based blacklist files are found. +## Used for "realtime" indexing setups. +gidblacklistdir string default="../gidblacklist" + +## A semicolon-separated list of index names and index name prefixes +## defining the set of indexes that are relevant when highlighting +## query keywords. The terms and phrases from the query targeting any +## of these indexes will be highlighted when dynamic teasers are +## generated. In order to separate index names and index name prefixes +## in the list, index name prefixes have a trailing '*'. Note that +## index aliases are treated like actual index names. This means that +## if you have an index relevant for highlighting and an index alias +## pointing to that index, you need to configure both as relevant for +## highlighting if you want to highlight keywords targeting both the +## actual index and the alias. Example config value: "normal*;title". +## Default config value: "*" (highlight all keywords). +highlightindexes string default="*" + +# ??? +hostname string default="" + +## provide a HTTP server at the given port number. +hport int default=8002 + +## If true, the TCP_NODELAY option is set on the http connections. +## This causes non-full packets to be sent even though previously sent +## data hasn't yet been acknowledged (e.g. due to the delayed ack +## feature present on various tcp stacks). +httpdnodelay bool default=false + +# ??? +intoccpoolsize int default=32768 +# ??? +intoccpoolstep int default=32768 +# ??? +jobqueuethreads int default=5 + +## Juniper configuration property map. +## currently known keys with defaults: +## [juniper.dynsum.highlight_on] = "<b>" +## [juniper.dynsum.highlight_off] = "</b>" +## [juniper.dynsum.continuation] = "..." +## [juniper.dynsum.length] = "256" +## [juniper.dynsum.min_length] = "128" +## [juniper.stem.min_length] = "5" +## [juniper.stem.max_extend] = "3" +## [juniper.dynsum.surround_max] = "128" +## [juniper.dynsum.max_matches] = "3" +## [juniper.dynsum.escape_markup] = "auto" +## [juniper.matcher.winsize] = "200" +## [juniper.dynsum.separators] = "\0x1F\0x1D" +## [juniper.dynsum.connectors] = "\0x1F\0x1D" +## [juniper.proximity.factor] = "0.25" +## [juniper.debug_mask] = "0" +## +#junipersetup properties + +## The maximum number of HTTP connections. +maxhttpconns int default=1024 + +## The maximum interval between a successful read from a socket +## before timeout, in seconds. +maxsocksilent double default=5.0 + +## The maximum number of threads to use. +maxthreads int default=100 + +## The maximum number of active requests at any time. Exceeding +## requests will be queued. +maxtransportconns int default=15 + +## If present the index to the document summary file (docsum.idx) +## is accessed on disk on each access instead of being cached in +## memory. For experimental use on systems with very many docu- +## ments but very few actual docsum requests. +nodocsumidxinmemory bool default=false + +## If set then locks on result cache are held only for very +## short intervals and only a single cache element is locked at a +## time. This simplifies the mutex locking order, but cause extra +## load due to queries that would previously first block then use +## cached value now being fully evaluted. +nonblockingresultcache bool default=false + +## A boolean value controlling removal of several common accented +## uses of characters, used when matching for highlighting. +normalize.accentremoval bool default=true + +## A boolean value controlling normalizing of LATIN CAPITAL/SMALL +## LIGATURE OE (U+0152 U+0153) to the string "oe", used when matching +## for highlighting. +normalize.ligaturesubstitution bool default=true + +## A boolean value controlling normalizing of various accented letters +## to two chars, for linguistics compatibility. +normalize.multicharexpansion bool default=true + +## A boolean value controlling normalizing of LATIN SMALL LETTER SHARP S +## (U+00DF) to the string "ss", used when matching for highlighting. +normalize.sharpssubstitution bool default=true + +## If true, queries are still handled during the reload operation +## (even when the document summaries have changed). If false then +## queries are stalled until reload has completed. +overlappedreload bool default=true + +## The partition number to report to the connecting fdispatch process +## if the dataset label didn't specify the partition number. +partition int default=0 + +## If set to "yes", use of proximity (cf. proximity and firstoccproximity) +## will affect phrases in addition to single words. If set to "no", +## use of proximity is never used for phrases. +phraseproximity string default="" + +## The file name to write the PID of the fsearch process to. +pidfile string default="" + +## Minimum value for maximum value of number of 'posocc' entries +## for a word. If set to 0, computed as 2 * binlow. +posbinhigh int default=0 + +## Maximum value for maximum value of number of 'posocc' entries +## for a word. If set to 0, computed as min(4 * binhigh, 0x7fffffff) +posbinlow int default=0 + +## The maximum value for number of 'posocc' entries for a word, +## specified as a percentage of the number of documents in the index. +## If more entries are needed for evaluation, posocc entries are not +## used for that word and evaluation will be performed without full +## proximity support. The percentage can be ramped off with the posbinlow +## and posbinhigh variables. If set to 0, computed as 2.0 * binsize. +posbinsize double default=0 + +## If set to "yes", use of posocc files is enabled, except when +## "forceemptyposoccs" is set or posocc files doesn't exist. If +## set to "no", use of posocc files is disabled. +proximity string default="" + +## Selects behavior when proximity can be used for two words but +## not three words while firstoccproximity can be used for three +## words. If set to "yes", then use proximity for two words. If +## set to "no", then use firstoccproximity for three words. +proximitypairbeforefirstoccproximitytriple string default="" + +## Selects behavior when proximity can be used for three words but +## not four words while firstoccproximity can be used for four +## words. If set to "yes", then use proximity for three words. If +## set to "no", then use firstoccproximity for four words. The +## default is "yes". +proximitytriplebeforefirstoccproximityquad string default="" + +## specifies the port number for the persistent internal transport +## protocol provided for a multi-level dispatch system. +ptport int default=8003 + +## a reference to a rank-profiles.def type configuration +## describing how to rank results. If empty, fsearch loads +## rank.cf from the dataset directory, see rank.cf(5). +rankcf reference + +## specifies a lower limit for the rankvalue of the results +## returned from the search node. +rankcutoff int default=0 + +## if set, an internal calculation is used for determining a +## rank cutoff value as above. +rankcutoffadvanced bool default=false + +## Specifies the constant value used in the internal advanced rank +## cutoff calculations done when the rankcutoffadvanced parameter is set. +## This roughly reflects the expected rank contribution of +## one good term. +rankcutoffadvval int default=0 + +# ??? +rankinginfopoolsize int default=1048576 +# ??? +rankinginfopoolstep int default=262144 +## Grace period (in seconds) after index reload where old index is +## still available. +reloadgraceperiod int default=64 + +## Maximum number of entries in the resultattributescache. +resultattributescachequeries int default=0 +## Maximum number of entries in the resultattributescache. +## 0 means no limitation. +resultattributescachesize int default=5000000 + +## The maximum lifetime of a resultcache element in seconds. +resultcachemaximumlifetime int default=7200 +## The minimum lifetime of a resultcache element in seconds. +resultcacheminimumlifetime int default=3600 + +## Maximum number of entries in the resultcache. +## 0 means no limitation. +resultcachequeries int default=0 +## Maximum size (in bytes) in the resultcache. +resultcachesize int default=50000000 + +# ??? +rewriter.indexes string default="" +# ??? +rewriter.langfield string default="bsumlanguage" +# ??? +rewriter.rootdir string default="" +# ??? +#rewritersetup properties + +## Set the number of samples a disk is marked as slow before +## starting selftest when no progress occurred during the last +## slowdisksamples samples. +slowdisklatch int default=1 + +## Set the number of milliseconds to sleep between each sample of +## disk state. +slowdisksamplemillisleep int default=100 + +## Set the number of nanoseconds to sleep between each sample of +## disk state. If 0, use slowdisksamplemillisleep instead. +## Restriction: This option only has effect on FreeBSD +## using the LinuxThreads port. +slowdisksamplenanosleep int default=0 + +## Set the number of contigous disk samples without progress and +## outstanding requests before a disk is detected as slow. +## If zero, automatic slowdisk detection is turned off. +## Recommended value is '20' (when using default values for the +## other slowdisk detection parameters). +slowdisksamples int default=0 + +# ??? +staticfilter string default="" + +## specifies a set of indexes for which to optimize exclusion +## handling. Colon is used to separate index names. Default is unset. +## The optimization use entries in the filter occurrence cache, thus +## a too small size limit of the cache will cause serious performance +## degradation. +staticfilterindexes string default="" + +# ??? +strictbind bool default=false + +## Specifies a file containing url coded queries to run as part of self +## test initiated when a slow disk has been detected. +testloadfile string default="" + +## the type of transport to use. Currently only "fnet" is available. +transport string default="" + +## Specifies the transport access log file used by the http server. +## The real log file name is created by using the strftime function +## with the given argument as template. +transportaccesslog string default="" + +## Specifies the interval between transport access log rotation. +## This is the number of minutes between log rotation, e.g a value +## of 1440 indicates that the log should be rotated every 24 hour. +transportaccesslogcycle int default=1440 + +## Specifies the offset from the start of each cycle when the +## transport access log should be cycled automatically. The unit +## is seconds, e.g. a value of 1020 indicates that the log should +## be cycled at 17 minutes past each cycle. +transportaccesslogcycleoffset int default=0 + +## Reference to VSM (Vespa Stream Matcher) configuration. If this is +## set, fsearch will run in VSM mode. +vsmconfig reference + +## If true, the TCP_NODELAY option is set on the persistent transport +## connections. This causes non-full packets to be sent even though +## previously sent data hasn't yet been acknowledged (e.g. due to the +## delayed ack feature present on various tcp stacks). +transportnodelay bool default=true + +# ??? +wordfolder bool default=false +# ??? +wordhashsize int default=524269 +# ??? +wordoccpoolsize int default=2097152 +# ??? +wordoccpoolstep int default=524288 +# ??? +wordpoolsize int default=262144 +# ??? +wordpoolstep int default=65536 + +## Connect spec for transactionlog server. +tlsspec string default="" + +## Document manager config +documentmanagerconfigid reference + +functionmodules[] string restart + +specialchars string + +tokenlist[].name string +tokenlist[].tokens[].token string +tokenlist[].tokens[].replace string default="" + +afloat double default=34 range=[0,1002] + +## qr-searchers: +tag.bold.open string default="<hi>" +tag.bold.close string default="</hi>" +tag.separator string default="<sep />" + +## This array contains the built-in searchers that should +## normally always run in a Vespa-S system. The actual list is +## in the global/ directory on the configserver. +builtin[].searcher string + +## If for some reason you need to disable one of the built-in +## searchers you can set this flag to "false". Handle with great +## care. You need to match the array index from the global/ +## directory, and this may change depending on versions. +builtin[].enabled bool default=true + +# some searcher specific configuration parameters: + +com.yahoo.prelude.searcher.FieldCollapsingSearcher.collapsesize int default=1 +com.yahoo.prelude.searcher.FieldCollapsingSearcher.extrafactor double default=2.0 +com.yahoo.prelude.searcher.FieldCollapsingSearcher.collapsefield string default="mid" + +com.yahoo.prelude.searcher.BlendingSearcher.numthreads int default=200 +com.yahoo.prelude.searcher.BlendingSearcher.docid string default="" + +com.yahoo.prelude.searcher.BoldingSearcher.source string default="" + +com.yahoo.prelude.searcher.JuniperSearcher.source string default="" +com.yahoo.prelude.searcher.JuniperSearcher.defaultdoctype string default="" + +## Query cache that can be placed anywhere in the search chain. Query/Result +## pairs (ie. entries) bigger than maxentrysizebytes will not be cached. +com.yahoo.prelude.searcher.CachingSearcher.cachesizemegabytes int default=100 +com.yahoo.prelude.searcher.CachingSearcher.timetoliveseconds int default=3600 +com.yahoo.prelude.searcher.CachingSearcher.maxentrysizebytes int default=10000 + +com.yahoo.prelude.searcher.XMLStringSearcher.source string default="" + +## relevancy as measured from the backend will usually be +## normalized into the [0,1000] range to make blending between +## several backends with different relevancy models possible; you +## can elect to skip this if you only have backends using +## relevancy scores that are directly comparable. +com.yahoo.prelude.fastsearch.FastSearcher.skipnormalizing bool default=true + +## how many aggregation groups to fetch from the backend +com.yahoo.prelude.grouping.AggregatingSearcher.maxgroups int default=100 + +com.yahoo.prelude.querytransform.PhrasingSearcher.automatonfile string default="" +com.yahoo.prelude.querytransform.NonPhrasingSearcher.automatonfile string default="" +com.yahoo.prelude.querytransform.TermReplacingSearcher.termlist[] string +com.yahoo.prelude.querytransform.CompleteBoostSearcher.source string default="" + +com.yahoo.prelude.querytransform.ExactStringSearcher.source string default="" +com.yahoo.prelude.querytransform.LiteralBoostSearcher.source string default="" +com.yahoo.prelude.querytransform.TermBoostSearcher.source string default="" +com.yahoo.prelude.querytransform.NormalizingSearcher.source string default="" +com.yahoo.prelude.querytransform.StemmingSearcher.source string default="" + +com.yahoo.prelude.statistics.StatisticsSearcher.latencybucketsize int default=30 + + +# here users may add their custom searchers +# (all strings should be class names) +customizedsearchers.rawquery[] string +customizedsearchers.transformedquery[] string +customizedsearchers.blendedresult[] string +customizedsearchers.unblendedresult[] string +customizedsearchers.backend[] string +customizedsearchers.argument[].key string +customizedsearchers.argument[].value string + +## This is for adding searchers which should be below BlendingSearcher, +## but not be linked to any Vespa cluster (directly). +external[].name string +external[].searcher[] string + +# Search cluster specific information. +## Name of search cluster. +searchcluster[].name string default="" + +## Names of search definitions served by search cluster. +searchcluster[].searchdef[] string + +## configid that may be used to get rank-profiles config for the cluster. +searchcluster[].rankprofiles.configid reference default="" + +## Indexing mode of search cluster. +searchcluster[].indexingmode enum { REALTIME, STREAMING } default=REALTIME + +## Storage cluster to use for search cluster if indexingmode is streaming. +searchcluster[].storagecluster string default="" + +# The available dispatchers on each search cluster +searchcluster[].dispatcher[].host string +searchcluster[].dispatcher[].port int + +## The number of least significant bits of the part id used to specify the +## row number (the rest of the bits specifies the column). Don't touch +## this unless you know why you are doing it. +searchcluster[].rowbits int default=0 + + +# 4 search-cluster-specific overrides of global cache parameters: + +## Internal searcher cache. Size is measured in megabytes of raw packet +## size. Hits larger than 1% of total cache size will not be cached. +searchcluster[].cache.size int default=1 + +## Timeout for internal searcher cache. Entries older than this number +## of seconds will be removed from cache. 0 means no cache timeout. +## If cachetimeoutseconds is used, the cache is not purged when reindexing. +searchcluster[].cache.timeout int default=-1 + +## If timeoutwithpurging is set, the index will be purged (possibly +## gradually) when index switching occurs, even if cache timeout > 0. +## Not used if cache timeout is 0. If searchcluster[].cache.timeout is +## not explicitly set, the global value will be used instead of the one +## set locally. +searchcluster[].cache.timeoutwithpurging bool default=false + +## Gradual cache switching causes the index to only be gradually purged +## when cache switching occurs. cacheswitchseconds is the length of the period +## when a given cache entry may be from either the previous or current +## index. Setting it to 0 makes QRS purges the cache entirely when a new +## index becomes available. At the end of the cacheswitchseconds period, +## the cache will be cleaned of any remaining entries from the previous +## index. +searchcluster[].cache.switchseconds int default=-1 + +# Per dispatcher config-id might be nice to have, remove it until needed. +# searchcluster[].dispatcher[].configid reference + +# rank-profiles +## name of this rank profile. maps to table index for internal use. +rankprofile[].name string + +## the name of a generic property available to the feature execution framework and feature plugins +rankprofile[].fef.property[].name string + +## the value of a generic property available to feature plugins +rankprofile[].fef.property[].value string + +## the catalog name overrides apply to +rankprofile[].catalog[].name string + +## Boost value for AND queries in this catalog. +rankprofile[].catalog[].andboost int default=0 + +## Boost value for OR queries in this catalog. +rankprofile[].catalog[].orboost int default=0 + +## Boost value for ANY queries in this catalog. +rankprofile[].catalog[].anyboost int default=0 + +## Boost value for NEAR queries in catalog. +rankprofile[].catalog[].nearboost int default=0 + +## Boost value for ORDEREDNEAR queries in this catalog. +rankprofile[].catalog[].orderednearboost int default=0 + +## Boost value for phrase queries in this catalog. +rankprofile[].catalog[].phraseboost int default=0 + +## Boost value for all queries in catalog. +rankprofile[].catalog[].rankboost int default=0 + +## If true, the context boost is the max value of +## the individual contextboosts. +## When false, the context boost when a term is in +## several contexts is the sum of the individual contextboosts. +rankprofile[].catalog[].bestcontextboostonly bool default=false + + +## If true, then use extnumoccboost only when calculating rank values. +## Also, do not normalize the extnumoccboost value with +## global term frequency. Default value is false. +rankprofile[].catalog[].extnumoccboostonly bool default=false + +## If yes, then use extnumoccboost only when calculating rank values. +## Also, do not normalize the extnumoccboost value with +## global term frequency. Default value is no. +rankprofile[].catalog[].numoccandextnumoccboostonly bool default=false + +## If yes, then use bitvectors when possible. +## Default value is false. +rankprofile[].catalog[].preferbitvector bool default=false + +## Load extnumoccboost for this catalog from the named file. +## extnumoccboost specifies boost values due to the number of +## occurences of a term that are external to the document. If +## "NULL" is given as file name, then all extnumoccboost values +## will be set to 0. +rankprofile[].catalog[].extnumoccboost.table string default="/home/vespa/conf/vespa/search/ranktables/constant-0000" + +## Load numoccboost for this catalog from the named file. +## numoccboost specifies boost values due to the number of occurences in +## a document. If "NULL" is given as file name, then all numoccboost +## values will be set to 0. +rankprofile[].catalog[].numoccboost.table string default="/home/vespa/conf/vespa/search/ranktables/constant-0000" + +## Load firstoccboost for catalog from the file named. +## firstoccboost specifies boost values due to the position of the +## first occurence in a document. If "NULL" is given as file name, +## then all firstoccboost values will be set to 0. +rankprofile[].catalog[].firstoccboost.table string default="/home/vespa/conf/vespa/search/ranktables/constant-0000" + + +## Load firstoccproximityboost for this catalog from the file named. +## firstoccproximity boost specifies boost values due to the correlation between +## positions of the first occurence in a document for two and two words. +## +## If "NULL" is given as file name, then all +## firstoccproximityboost values will be set to 0. If otherwise set, +## should be the name of a file to load into the table. The file +## should have 256 lines each containing a single integer. +## +## There are 256 elements in the table, handling forward distances from 1. +## The corresponding firstoccrevproximityboost table is used +## to handle closeness in reverse order. +## +## The last array index specifies the proximity table set. During +## evaluation, the bigram proximity weight supplied by the query segmenter +## specifies which proximity table set to use, with a fallback to set 0 +## when no information is available. +rankprofile[].catalog[].firstoccproximityboost[].table string default="/home/vespa/conf/vespa/search/ranktables/constant-0000" + +## Load firstoccrevproximityboost table for this catalog from the named file. +## Specifies boost values due to the correlation between positions +## of the first occurence in a document for two and two words when +## the second word in the query comes first in the document. +## See also firstoccproximityboost above. +rankprofile[].catalog[].firstoccrevproximityboost[].table string default="/home/vespa/conf/vespa/search/ranktables/constant-0000" + +## Load proximityboost for this catalog from the named file. +## proximity boost specifies boost values due to the correlation between +## positions of the occurences in a document for two and two words. +## See also firstoccproximityboost above. +rankprofile[].catalog[].proximityboost[].table string default="/home/vespa/conf/vespa/search/ranktables/constant-0000" + +## Load revproximityboost for this catalog from the named file. +## revproximity boost specifies boost values due to the correlation between +## positions of the occurences in a document for two and two words. +## See also firstoccproximityboost above. +rankprofile[].catalog[].revproximityboost[].table string default="/home/vespa/conf/vespa/search/ranktables/constant-0000" + +## Load divtable for this catalog from the named file. +## Rank values for a query term are divided by the entry +## in divtable indexed by log2 of term frequence. +## The file should contain ?? lines each with a single integer. +rankprofile[].catalog[].divtable string default="" + +## The name of a context in this catalog to specify boosts for. +rankprofile[].catalog[].context[].name string + +## Boost occurrences in this context with the given value. +## XXX -1 uses default (???) from somewhere(TM). +rankprofile[].catalog[].context[].contextboost int default=0 + +## Boost pair of occurrences in this context with +## the given value when evaluating 2 words from same catalog in +## parallell. +## XXX -1 uses default (???) from somewhere(TM). +rankprofile[].catalog[].context[].commoncontextboost.pair int default=0 + +## Boost triple of occurrences in this context with +## the given value when evaluating 3 words from same catalog in +## parallell. +## XXX -1 uses default (???) from somewhere(TM). +rankprofile[].catalog[].context[].commoncontextboost.triple int default=0 + +## Boost quad of occurrences in this context with +## the given value when evaluating 4 words from same catalog in +## parallell. +## XXX -1 uses default (???) from somewhere(TM). +rankprofile[].catalog[].context[].commoncontextboost.quad int default=0 + + +## The name of the attribute +rankprofile[].attribute[].name string + +## Boost value for queries that hit in this attribute +rankprofile[].attribute[].attributecontextboost int default=0 + +## Load weightboost for this attribute from the named file. +## weightboost specifies boost values due to the weight (weighted set) +## or number of occurences (single, array) in an attribute. +## If "NULL" is given as file name, then all weightboost values will be set to 0. +rankprofile[].attribute[].weightboost.table string default="/home/vespa/conf/vespa/search/ranktables/constant-0000" + + +## Load static rank values from the given staticrank docattr vector. +## Must be specified in index.cf as a staticrankfile. +rankprofile[].staticrankfile string default="" + +## Multiply static rank values with given value when calculating total +## rank value. +rankprofile[].staticcoefficient int default=1 + +## If false then use only static ranking when sorting result hits. +## Default is true. +rankprofile[].dynamicranking bool default=true + +## If dynamic ranking is turned off, then ascending will sort the +## result hits with lowest static rank values first, while +## descending will sort with highest static rank values +## first. Default is descending. This keyword has no effect if +## dynamic ranking is on. +rankprofile[].staticranksortorder string default="descending" + +## Load static rank mapping from the file named table. The static +## rank mapping maps each 8-bit static rank value into a 32-bit static +## rank value. This option may only be used with 8-bit static rank files. +rankprofile[].staticrankmap string default="" + +## If set to "true", total rank will be reduced when dynamic rank is less than +## 25% of static rank, to suppress irrelevant hits from popular sites. +## If set to "false", total rank is not reduced. +rankprofile[].clampstaticrank bool default=false + +## Load document datetime values used for freshness boost calculation from +## this file. The values must be coded as minutes since +## 1900-01-01T00:00Z. The value 0 has the special meaning +## "no datetime value exists". +rankprofile[].freshnessboost.file string default="" + +## Load freshnessboost lookup-table values from the file named +## table instead of using built-in default values. The file must +## contain 32 white-space separated non-negative integers. +rankprofile[].freshnessboost.table string default="/home/vespa/conf/vespa/search/ranktables/constant-0000" + +## When calculating the freshness boost value multiply difference between +## current datetime and document datetime with timeoffset before taking +## the base-2 logarithm. Default value is 1. Max value is 31. +rankprofile[].freshnessboost.timeoffset int default=1 + +## If a document has datetime value 0, then use defaultboostvalue +## as freshness boost value instead of doing table lookup. The default +## default value is 0 (no boost). +rankprofile[].freshnessboost.defaultboostvalue int default=0 + +## Multiply freshness boost value with coefficient when calculating +## total freshness boost value. If coefficient 0 is used, no freshness +## boost value will be computed or added. Default value is 0. +rankprofile[].freshnessboost.coefficient int default=0 + +## boost table files for distance ranking, 1 dimension. +## The tables have 465 elements each, where slots 0..15 represents +## distances 0..15 while the remaining slots represents distance +## (16 + (slot & 15)) << ((slot >> 4) - 1). Linear interpolation is +## used for distances "between" table slots. +## +## If "NULL" is given as the file name then all 1D distance boost values +## for that table will be set to 0. +rankprofile[].distance1dboosttable[].table string + +## boost table files for distance ranking, 2 dimensions. +## The tables have 977 elements each, where slots 0..15 represents +## square of distance being 0..15 while the remaining slots represents +## square of distance distance being +## (16 + (slot & 15)) << ((slot >> 4) - 1). Linear interpolation is +## used for distances "between" table slots. +## +## If "NULL" is given as the file name then all 2D distance boost values +## for that table will be set to 0. +rankprofile[].distance2dboosttable[].table string + +## The lowest possible size of a ranked result. This is the lower ramp +## of the percentage specified in the binsize variable. The default is +## specified in fsearchrc. +rankprofile[].binlow int default=-1 + +## The high limit of the ranked result bin. If the percentage of the +## resultset specified in binsize is higher than this limit, this will be +## the max size. The default is specified in fsearchrc. +rankprofile[].binhigh int default=-1 + +## The size of the ranked results as a percentage of the total result +## set size. The percentage can be ramped off with the binlow and binhigh +## variables. The default is specified in fsearchrc. +rankprofile[].binsize double default=-1 + +## Minimum value for maximum value of number of 'posocc' entries for a word. +## The default is specified in fsearchrc. +rankprofile[].posbinlow int default=-1 + +## Maximum value for maximum value of number of 'posocc' entries for a word. +## The default is specified in fsearchrc. +rankprofile[].posbinhigh int default=-1 + +## The maximum value for number of 'posocc' entries for a word, specified +## as a percentage of the number of documents in the index. If more +## entries are needed for evaluation, posocc entries are not used for that +## word and evaluation will be performed without full proximity support. +## The percentage can be ramped off with the posbinlow and posbinhigh +## variables. The default is specified in fsearchrc. +rankprofile[].posbinsize int default=-1 + +## After all other rank calculations, the rank value is tuned according +## to the tunefactor and tunebias values. The rank value is modified +## as follows: new_rank = old_rank * tunefactor + tunebias. +rankprofile[].tunefactor double default=1.0 + +## After all other rank calculations, the rank value is tuned according +## to the tunefactor and tunebias values. The rank value is modified +## as follows: new_rank = old_rank * tunefactor + tunebias. +rankprofile[].tunebias int default=0 + +## A lower limit for the rankvalue of the results returned from the +## search node. If rankcutoff.advanced is set to "true", determines +## the constant value used in the internal advanced rank cutoff +## calculations. This roughly reflects the expected rank contribution +## of one good term. +## The rankcutoff.val value and the rankcutoff.advanced parameter +## may be used if you only want hits with a minimum relevancy to show +## up in the resultset. +## A value below zero means no rankcutoff is done. +rankprofile[].rankcutoff.val int default=-1 + +## When rankcutoff.val is in use, this flag controls whether to use +## an internal calculation is used for determining the rank cutoff +## value. If "false", use rankcutoff.val as a direct lower limit. +rankprofile[].rankcutoff.advanced bool default=false + +## If set to "ON", use of posocc files is enabled, except when +## "forceemptyposoccs" is set in fsearchrc or posocc files doesn't exist. +## If set to "OFF", use of posocc files is disabled. +## If "NOTSET" the fsearchrc "proximity" parameter is used instead. +rankprofile[].proximity.full.enable enum { OFF, ON, NOTSET } default=NOTSET + +## If set to "ON", use of firstoccproximity is enabled. +## If set to "OFF", use of firstoccproximity is disabled. +## When NOTSET use the firstoccproximity value in fsearchrc configuration. +rankprofile[].proximity.firstocc.enable enum { OFF, ON, NOTSET } default=NOTSET + +## If set to "ON", use of proximity (cf. proximity and firstoccproximity) +## will affect phrases in addition to single words. +## If set to "OFF", proximity is never used for phrases. +## When NOTSET use the phraseproximity value in fsearchrc configuration. +rankprofile[].proximity.phrase.enable enum { OFF, ON, NOTSET } default=NOTSET + +## Selects behavior when proximity can be used for two words but not three +## words while firstoccproximity can be used for three words. +## If set to "ON", then use proximity for two words. +## If set to "OFF", then use firstoccproximity for three words. +## When NOTSET use the proximitypairbeforefirstoccproximitytriple value +## in fsearchrc configuration. +rankprofile[].proximity.pairbeforefirstocctriple.enable enum { OFF, ON, NOTSET } default=NOTSET + +## Selects behavior when proximity can be used for three words but not four +## words while firstoccproximity can be used for four words. +## If set to "ON", then use proximity for three words. +## If set to "OFF", then use firstoccproximity for four words. +## When NOTSET use the proximitytriplebeforefirstoccproximityquad value +rankprofile[].proximity.triplebeforefirstoccquad.enable enum { OFF, ON, NOTSET } default=NOTSET diff --git a/config/src/test/java/com/yahoo/vespa/config/configsglobal/qr-logging.cfg b/config/src/test/java/com/yahoo/vespa/config/configsglobal/qr-logging.cfg new file mode 100755 index 00000000000..cc640619e3a --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/configsglobal/qr-logging.cfg @@ -0,0 +1,44 @@ +logger com.yahoo +speciallog[6] +speciallog[0].name QueryAccessLog +speciallog[0].type file +speciallog[0].filehandler.name QueryAccessLog +speciallog[0].filehandler.pattern logs/vespa/qrs/QueryAccessLog.%Y%m%d%H%M%S +speciallog[0].filehandler.rotation "0 60 ..." +speciallog[0].cachehandler.name QueryAccessLog +speciallog[0].cachehandler.size 1000 +speciallog[1].name QueryResultLog +speciallog[1].type cache +speciallog[1].filehandler.name QueryResultLog +speciallog[1].filehandler.pattern logs/vespa/qrs/QueryResultLog.%Y%m%d%H%M%S +speciallog[1].filehandler.rotation "0 60 ..." +speciallog[1].cachehandler.name QueryResultLog +speciallog[1].cachehandler.size 1000 +speciallog[2].name ResultImpressionLog +speciallog[2].type file +speciallog[2].filehandler.name ResultImpressionLog +speciallog[2].filehandler.pattern logs/vespa/qrs/ResultImpressionLog.%Y%m%d%H%M%S +speciallog[2].filehandler.rotation "0 60 ..." +speciallog[2].cachehandler.name ResultImpressionLog +speciallog[2].cachehandler.size 1000 +speciallog[3].name ServiceEventLog +speciallog[3].type cache +speciallog[3].filehandler.name ServiceEventLog +speciallog[3].filehandler.pattern logs/vespa/qrs/ServiceEventLog.%Y%m%d%H%M%S +speciallog[3].filehandler.rotation "0 60 ..." +speciallog[3].cachehandler.name ServiceEventLog +speciallog[3].cachehandler.size 1000 +speciallog[4].name ServiceStatusLog +speciallog[4].type off +speciallog[4].filehandler.name ServiceStatusLog +speciallog[4].filehandler.pattern logs/vespa/qrs/ServiceStatusLog.%Y%m%d%H%M%S +speciallog[4].filehandler.rotation "0 60 ..." +speciallog[4].cachehandler.name ServiceStatusLog +speciallog[4].cachehandler.size 1000 +speciallog[5].name ServiceTraceLog +speciallog[5].type parent +speciallog[5].filehandler.name ServiceTraceLog +speciallog[5].filehandler.pattern logs/vespa/qrs/ServiceTraceLog.%Y%m%d%H%M%S +speciallog[5].filehandler.rotation "0 60 ..." +speciallog[5].cachehandler.name ServiceTraceLog +speciallog[5].cachehandler.size 1000 diff --git a/config/src/test/java/com/yahoo/vespa/config/configsglobal/qr-templates.3.cfg b/config/src/test/java/com/yahoo/vespa/config/configsglobal/qr-templates.3.cfg new file mode 100644 index 00000000000..345c20157f9 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/configsglobal/qr-templates.3.cfg @@ -0,0 +1,111 @@ +washing -0.45 +washer 0 +hey[2] +hey[0].ho[1] +hey[0].ho[0].lets[3] +hey[0].ho[0].lets[0].go "slayer" +hey[0].ho[0].lets[0].fishing -1 +hey[0].ho[0].lets[0].ref JA +hey[0].ho[0].lets[1].go "gate" +hey[0].ho[0].lets[1].ref :parent: +hey[0].ho[0].lets[str].go "strung" +hey[0].ho[0].me 0.0 +hey[fooo].ho[0] + +hey[0] 78 + +longVal 500 +longWithRange -9000000000 +longArr[2] +longArr[0] 0 +longArr[1] 1 +longArrWithRange[1] +longArrWithRange[0] 9000000000 + +bar.arrline[0] "foo" +bar.arrline[1] "bar" + +fileVal "/nodefault/file" +fileArr[2] +fileArr[0] "keyb.com" +fileArr[1] "2b4a64d0cb36d44ba8a52506d9fe480bf3511be6" + +urlprefix "foo" +templateset[2] +templateset[0].urlprefix "/basic" +#templateset[0].mimetype "text/xml" +templateset[0].encoding "ut\"f-8\n' <hit relevancy=\"$relevancy\">\n#foreach" +templateset[0].headertemplate "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<resultset totalhits=\"$result.hitCount\">\n" +templateset[0].footertemplate "</resultset>\n" +templateset[0].nohitstemplate "<empty/>\n" +templateset[0].hittemplate "<hit relevancy=\"$relevancy\">\n#foreach( $key in $hit.getPropertyKeySet() )\n <field name='$key'>$hit.getPropertyXML($key)</field>\n#end\n</hit>\n" +templateset[0].errortemplate "<ERROR CODE=\"$result.error.code\">$result.error.message</ERROR>\n" +templateset[1].urlprefix "/xsearch" +templateset[1].mimetype "text/xml" +templateset[1].encoding "utf-8" +templateset[1].rankprofile 0 +templateset[1].headertemplate "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<RESULTSET TOTALHITS=\"$result.hitCount\">\n" +templateset[1].footertemplate "</RESULTSET>\n" +templateset[1].nohitstemplate "0.56" +templateset[1].hittemplate "<HIT RELEVANCY=\"$relevancy\" TYPE=\"$hit.typeString\">\n<FIELD NAME=\"uri\">$uri</FIELD>\n<FIELD NAME=\"category\">$category</FIELD>\n<FIELD NAME=\"bsumtitle\">$bsumtitle</FIELD>\n</HIT>\n" +templateset[1].errortemplate "45" +config[0].id :parent: +config[0].autostart +hi[0].there[0].e BATCH +ilscript[music].name music +ilscript[music].doctype music +ilscript[music].content[1] +ilscript[music].content[0] "\"music\" | summary sddocname | lowercase | index sddocname;" + +musum * + +auran "value=\"Confirm\"/></form>\"\"Tuna - step three." + +route[1] +route[0].name "search/cluster.books2" +route[0].selector "books.isbn=\"none\"" +route[0].feed "books" + +languages[3] +languages[2] "swahili" + +languages2[2] +languages2[0] "swedish" + +foolang[5] +foolang[0].lang[3] +foolang[0].lang[1] "Swahili" +foolang[1].lang[3] +foolang[2].lang[3] +foolang[2].lang[0] "Setswana" +foolang[3].lang[4] +foolang[3].lang[3] "Norwegian" +foolang[4].lang[2] + +myIntMap{"foo"} 67 +myIntMap{"bar"} 68 +myStringMap{"fo"} "gh" +myStringMap{"ba"} "ij" +myStructMap{"FOO"}.myInt 78 +myStructMap{"FOO"}.myString "myFoo" +myStructMap{"FOOO"}.myInt 89 +myStructMap{"FOOO"}.myString "myFooS" +myStructMap{"FOOO"}.myIntDef -99 +myStructMap{"FOOO"}.myStringDef "myFooSBall" + +myStructMap{"FOO"}.myStruct.a "guitar" +myStructMap{"FOO"}.myStruct.c /tmp + +myStructMap{"FOOO"}.myStruct.a "bass" +myStructMap{"FOOO"}.myStruct.b "drums" +myStructMap{"FOOO"}.myStruct.c /var/log + +myStructMap{"FOO"}.myStructNestedArray[0] -9 +myStructMap{"FOO"}.myStructNestedArray[1] -10 + +myStructMap{"FOO"}.myNestedLeafMap{"Nested1"} 90 +myStructMap{"FOO"}.myNestedLeafMap{"Nested2"} 9000 + +myStructMap{"FOO"}.myNestedMap{"Nested3"}.myLong 809 +myStructMap{"FOO"}.myNestedMap{"Nested3"}.myLongDef 810 +myStructMap{"FOO"}.myNestedMap{"Nested4"}.myLong 811 diff --git a/config/src/test/java/com/yahoo/vespa/config/configsglobal/testfoobar.12.cfg b/config/src/test/java/com/yahoo/vespa/config/configsglobal/testfoobar.12.cfg new file mode 100644 index 00000000000..3d23f7fd29a --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/configsglobal/testfoobar.12.cfg @@ -0,0 +1,105 @@ +vh[5] +vh[0] 0.3345 +vh[1] -0.3 +vh[2] 134.3 +vh[3] 234.34 +vh[4] -24.3 +bg[5] +bg[0] 5 +bg[1] +bg[2] 10 +bg[3] 4 +bg[4] -2 +gee[2] +gee[0] "jabbaTuna" +gee[1] jabba:500/dusteri +storage[2] +storage[0].distributor[2] +storage[0].distributor[0] "tra" +storage[0].distributor[1] "de" +storage[0].feeder[1] +storage[0].feeder[0] "li" +#storage[0].feeder[1] "dum" +storage[1].distributor[3] +storage[1].distributor[0] "Etra" +storage[1].distributor[1] "Ede" +#storage[1].feeder[3] +storage[1].feeder[0] "Eli" +storage[1].feeder[1] "Edum" +storage[1].feeder[2] "TEdum" + +#ju[].hu[].tu[] double default=45 range=[0,100.1] +#ju[].hu[].wu[] enum { HEY, HO} default=HO +#ju[].hu[].tang[] bool default=false +#ju[].hu[].clan[] int default=45 range=[-90,90] + +ju[2] +ju[0].hu[2] +ju[0].hu[0].tu[2] +ju[0].hu[0].tu[0] 45.0 +ju[0].hu[0].tu[1] 0.0 +ju[0].hu[0].tang[2] +ju[0].hu[0].tang[0] true +#ju[0].hu[0].tang[1] 0.0 + +#ju[0].hu[1].tu[2] +ju[0].hu[1].tu[0] 667.865 +ju[0].hu[0].wu[2] +ju[0].hu[0].wu[0] HEY +ju[0].hu[0].wu[1] HEY +ju[0].hu[1].wu[1] +ju[0].hu[1].wu[0] HO + +ju[1].hu[2] +ju[1].hu[0].tu[2] +ju[1].hu[0].tu[0] 78 +ju[1].hu[0].tu[1] 78.9 +ju[1].hu[1].tu[1] +ju[1].hu[1].tu[0] 88.9 + +ju[1].hu[0].wu[3] +ju[1].hu[0].wu[0] HEY +ju[1].hu[0].wu[1] HEY +ju[1].hu[0].wu[2] HO +ju[1].hu[1].wu[1] +ju[1].hu[1].wu[0] HO +ju[1].hu[1].clan[4] +ju[1].hu[1].clan[0] 5 +ju[1].hu[1].clan[1] 6 +ju[1].hu[1].clan[2] 7 +ju[1].hu[1].clan[3] 8 +foo "123aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa gh:sann\" da" +headertemplate "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<html>\n<head>\n<title>ranqualizer:$hostname:$profile:</title>\n<link type=\"text/css\" rel=\"stylesheet\" href=\"/layout.css\"/>\n<meta name=\"ROBOTS\" content=\"NOINDEX,NOFOLLOW\"/><meta http-equiv=\"Cache-control\" content=\"no-cache\"/><meta http-equiv=\"Cache-control\" content=\"must-revalidate\"/><meta http-equiv=\"Cache-control\" content=\"max-age=0\"/><meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\"/><link rel=\"icon\" href=\"/favicon.ico\"/><script type=\"text/javascript\">function show(foo,f){document.getElementById(foo).style.display=\"block\";} function hide(foo,f){document.getElementById(foo).style.display = \"none\";}</script></head>\n<body>\n" +ptport 10108 +rankcf :parent: +hport 10109 +frtport 10107 +transportnodelay true +partition 0 +datasetdir none +proximity "yes" +vsmconfig "" +documentmanagerconfigid :parent: +include: search/cluster.music +include: search/cluster.music2 + +rankprofile[extra].fef.property[4] +rankprofile[ignore].fef.property[4].name "vespa.dump.ignoredefaultfeatures" +rankprofile[ignore].fef.property[4].value true + +specialchars "索 索尼 DVD±R" + +tokenlist[1] +tokenlist[0].name "default" +tokenlist[0].tokens[8] +tokenlist[0].tokens[0].token "c++" +tokenlist[0].tokens[1].token "wal-mart" +tokenlist[0].tokens[1].replace "walmart" +tokenlist[0].tokens[2].token ".net" +tokenlist[0].tokens[3].token "-索" +tokenlist[0].tokens[4].token "sony" +tokenlist[0].tokens[4].replace "索尼" +tokenlist[0].tokens[5].token "dvd+-r" +tokenlist[0].tokens[6].token "DVD±R" +tokenlist[0].tokens[7].token "dvdplusminusr" +tokenlist[0].tokens[7].replace "dvd+-r" diff --git a/config/src/test/java/com/yahoo/vespa/config/protocol/ConfigResponseTest.java b/config/src/test/java/com/yahoo/vespa/config/protocol/ConfigResponseTest.java new file mode 100644 index 00000000000..e39c0a6f623 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/protocol/ConfigResponseTest.java @@ -0,0 +1,66 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.protocol; + +import com.yahoo.foo.SimpletypesConfig; +import com.yahoo.config.codegen.DefParser; +import com.yahoo.config.codegen.InnerCNode; +import com.yahoo.text.StringUtilities; +import com.yahoo.text.Utf8Array; +import com.yahoo.vespa.config.ConfigPayload; +import com.yahoo.vespa.config.LZ4PayloadCompressor; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; + +/** + * @author lulf + * @since 5.1 + */ +public class ConfigResponseTest { + + @Test + public void require_that_slime_response_is_initialized() throws IOException { + ConfigPayload configPayload = ConfigPayload.fromInstance(new SimpletypesConfig(new SimpletypesConfig.Builder())); + DefParser dParser = new DefParser(SimpletypesConfig.getDefName(), new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))); + InnerCNode targetDef = dParser.getTree(); + ConfigResponse response = SlimeConfigResponse.fromConfigPayload(configPayload, targetDef, 3, "mymd5"); + List<String> payload = response.getLegacyPayload(); + assertNotNull(payload); + assertThat(payload.size(), is(6)); + assertThat(payload.get(0), is("boolval false")); + assertThat(response.getGeneration(), is(3l)); + assertThat(response.getConfigMd5(), is("mymd5")); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + response.serialize(baos, CompressionType.UNCOMPRESSED); + assertThat(baos.toString(), is("{\"boolval\":false,\"doubleval\":0.0,\"enumval\":\"VAL1\",\"intval\":0,\"longval\":0,\"stringval\":\"s\"}")); + } + + @Test + public void require_that_slime_response_decompresses_on_serialize() throws IOException { + + ConfigPayload configPayload = ConfigPayload.fromInstance(new SimpletypesConfig(new SimpletypesConfig.Builder())); + DefParser dParser = new DefParser(SimpletypesConfig.getDefName(), new StringReader(StringUtilities.implode(SimpletypesConfig.CONFIG_DEF_SCHEMA, "\n"))); + InnerCNode targetDef = dParser.getTree(); + Utf8Array data = configPayload.toUtf8Array(true); + Utf8Array bytes = new Utf8Array(new LZ4PayloadCompressor().compress(data.getBytes())); + ConfigResponse response = new SlimeConfigResponse(bytes, targetDef, 3, "mymd5", CompressionInfo.create(CompressionType.LZ4, data.getByteLength())); + List<String> payload = response.getLegacyPayload(); + assertNotNull(payload); + assertThat(payload.size(), is(6)); + assertThat(payload.get(0), is("boolval false")); + assertThat(response.getGeneration(), is(3l)); + assertThat(response.getConfigMd5(), is("mymd5")); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + response.serialize(baos, CompressionType.UNCOMPRESSED); + assertThat(baos.toString(), is("{\"boolval\":false,\"doubleval\":0.0,\"enumval\":\"VAL1\",\"intval\":0,\"longval\":0,\"stringval\":\"s\"}")); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestBase.java b/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestBase.java new file mode 100644 index 00000000000..0cf6491432d --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestBase.java @@ -0,0 +1,286 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.protocol; + +import com.yahoo.foo.SimpletypesConfig; +import com.yahoo.config.subscription.ConfigSet; +import com.yahoo.config.subscription.ConfigSourceSet; +import com.yahoo.config.subscription.ConfigSubscriber; +import com.yahoo.config.subscription.impl.GenericConfigSubscriber; +import com.yahoo.config.subscription.impl.JRTConfigRequester; +import com.yahoo.config.subscription.impl.JRTConfigSubscription; +import com.yahoo.config.subscription.impl.MockConnection; +import com.yahoo.jrt.Request; +import com.yahoo.slime.Inspector; +import com.yahoo.slime.JsonDecoder; +import com.yahoo.slime.Slime; +import com.yahoo.test.ManualClock; +import com.yahoo.text.Utf8; +import com.yahoo.vespa.config.ConfigKey; +import com.yahoo.vespa.config.ConfigPayload; +import com.yahoo.vespa.config.ErrorCode; +import com.yahoo.vespa.config.RawConfig; +import com.yahoo.vespa.config.TimingValues; +import com.yahoo.vespa.config.util.ConfigUtils; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Collections; +import java.util.Optional; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author lulf + * @since 5.3 + */ +public abstract class JRTConfigRequestBase { + + protected String defName = "mydef"; + protected String defNamespace = "my.name.space"; + protected String hostname = "myhost"; + protected String configId = "config/id"; + protected String defMd5 = "595f44fec1e92a71d3e9e77456ba80d1"; + protected long currentGeneration = 3; + protected final Optional<VespaVersion> vespaVersion = Optional.of(VespaVersion.fromString("5.38.24")); + protected long timeout = 5000; + protected Trace trace ; + protected String configMd5 = ConfigUtils.getMd5(createPayload().getData()); + protected JRTClientConfigRequest clientReq; + protected JRTServerConfigRequest serverReq; + + @Before + public void setupRequest() throws IOException { + clientReq = createReq(); + serverReq = createReq(clientReq.getRequest()); + assertTrue(serverReq.validateParameters()); + } + + private JRTClientConfigRequest createReq() throws IOException { + trace = Trace.createNew(3, new ManualClock()); + trace.trace(1, "hei"); + return createReq(defName, defNamespace, defMd5, hostname, configId, configMd5, currentGeneration, timeout, trace); + } + + private JRTClientConfigRequest createReq(Payload payload) throws IOException { + trace = Trace.createNew(3, new ManualClock()); + trace.trace(1, "hei"); + return createReq(defName, defNamespace, defMd5, hostname, configId, ConfigUtils.getMd5(payload.getData()), currentGeneration, timeout, trace); + } + + protected abstract JRTClientConfigRequest createReq(String defName, String defNamespace, String defMd5, String hostname, String configId, String configMd5, long currentGeneration, long timeout, Trace trace) throws IOException; + protected abstract JRTServerConfigRequest createReq(Request request); + protected abstract JRTClientConfigRequest createReq(JRTConfigSubscription<SimpletypesConfig> sub, Trace aNew); + protected abstract JRTClientConfigRequest createFromRaw(RawConfig rawConfig, long serverTimeout, Trace aNew); + + protected void request_is_parsed_base() { + String [] expectedContent = new String[]{ + "namespace=my.name.space", + "myfield string" + }; + System.out.println(serverReq.toString()); + assertThat(serverReq.getConfigKey().getName(), is(defName)); + assertThat(serverReq.getConfigKey().getNamespace(), is(defNamespace)); + assertThat(serverReq.getConfigKey().getMd5(), is(defMd5)); + assertThat(serverReq.getConfigKey().getConfigId(), is(configId)); + assertThat(serverReq.getDefContent().asStringArray(), is(expectedContent)); + assertFalse(serverReq.noCache()); + assertTrue(serverReq.getRequestTrace().toString().contains("hi")); + assertThat(serverReq.getRequestConfigMd5(), is(configMd5)); + assertThat(serverReq.getRequestGeneration(), is(currentGeneration)); + } + + @Test + public void delay_mechanisms_functions() { + assertFalse(serverReq.isDelayedResponse()); + serverReq.setDelayedResponse(true); + assertTrue(serverReq.isDelayedResponse()); + serverReq.setDelayedResponse(false); + assertFalse(serverReq.isDelayedResponse()); + } + + public JRTServerConfigRequest next_request_is_correct_base() { + String [] expectedContent = new String[]{ + "namespace=my.name.space", + "myfield string" + }; + JRTServerConfigRequest next = createReq(clientReq.nextRequest(6).getRequest()); + assertThat(next.getConfigKey().getName(), is(defName)); + assertThat(next.getConfigKey().getNamespace(), is(defNamespace)); + assertThat(next.getConfigKey().getMd5(), is(defMd5)); + assertThat(next.getConfigKey().getConfigId(), is(configId)); + assertThat(next.getDefContent().asStringArray(), is(expectedContent)); + assertFalse(next.noCache()); + assertThat(next.getTimeout(), is(6l)); + assertThat(next.getTimeout(), is(6l)); + return next; + } + + + @Test + public void next_request_when_error_is_correct() { + serverReq.addOkResponse(createPayload(), 999999, "newmd5"); + serverReq.addErrorResponse(ErrorCode.OUTDATED_CONFIG, "error message"); + System.out.println(serverReq); + JRTClientConfigRequest next = clientReq.nextRequest(6); + System.out.println(next); + // Should use config md5 and generation from the request, not the response + // when there are errors + assertThat(next.getRequestConfigMd5(), is(clientReq.getRequestConfigMd5())); + assertThat(next.getRequestGeneration(), is(clientReq.getRequestGeneration())); + } + + @Test + public void ok_response_is_added() { + Payload payload = createPayload("vale"); + String md5 = ConfigUtils.getMd5(payload.getData()); + long generation = 4l; + serverReq.addOkResponse(payload, generation, md5); + assertTrue(clientReq.validateResponse()); + assertThat(clientReq.getNewPayload().withCompression(CompressionType.UNCOMPRESSED).getData().toString(), is(payload.getData().toString())); + assertThat(clientReq.getNewGeneration(), is(4l)); + assertThat(clientReq.getNewConfigMd5(), is(md5)); + assertTrue(clientReq.hasUpdatedConfig()); + assertTrue(clientReq.hasUpdatedGeneration()); + } + + @Test + public void error_response_adds_common_elements() { + serverReq.addErrorResponse(ErrorCode.APPLICATION_NOT_LOADED, ErrorCode.getName(ErrorCode.APPLICATION_NOT_LOADED)); + assertThat(serverReq.getRequest().returnValues().size(), is(1)); + Slime data = new JsonDecoder().decode(new Slime(), Utf8.toBytes(serverReq.getRequest().returnValues().get(0).asString())); + Inspector response = data.get(); + assertThat(response.field(SlimeResponseData.RESPONSE_DEF_NAME).asString(), is(defName)); + assertThat(response.field(SlimeResponseData.RESPONSE_DEF_NAMESPACE).asString(), is(defNamespace)); + assertThat(response.field(SlimeResponseData.RESPONSE_DEF_MD5).asString(), is(defMd5)); + assertThat(response.field(SlimeResponseData.RESPONSE_CONFIGID).asString(), is(configId)); + assertThat(response.field(SlimeResponseData.RESPONSE_CLIENT_HOSTNAME).asString(), is(hostname)); + Trace t = Trace.fromSlime(response.field(SlimeResponseData.RESPONSE_TRACE)); + assertThat(t.toString(), is(trace.toString())); + } + + @Test + public void generation_only_is_updated() { + Payload payload = createPayload(); + serverReq.addOkResponse(payload, 4l, ConfigUtils.getMd5(payload.getData())); + boolean value = clientReq.validateResponse(); + assertTrue(clientReq.errorMessage(), value); + assertFalse(clientReq.hasUpdatedConfig()); + assertTrue(clientReq.hasUpdatedGeneration()); + } + + protected static Payload createPayload() { + return createPayload("bar"); + } + + private static Payload createPayload(String value) { + Slime slime = new Slime(); + slime.setObject().setString("myfield", value); + return Payload.from(new ConfigPayload(slime)); + } + + @Test + public void nothing_is_updated() { + Payload payload = createPayload(); + serverReq.addOkResponse(payload, currentGeneration, configMd5); + assertTrue(clientReq.validateResponse()); + assertFalse(clientReq.hasUpdatedConfig()); + assertFalse(clientReq.hasUpdatedGeneration()); + } + + @Test + public void payload_is_empty() throws IOException { + Payload payload = Payload.from(ConfigPayload.empty()); + clientReq = createReq(payload); + serverReq = createReq(clientReq.getRequest()); + serverReq.addOkResponse(payload, currentGeneration, ConfigUtils.getMd5(payload.getData())); + boolean val = clientReq.validateResponse(); + assertTrue(clientReq.errorMessage(), val); + assertFalse(clientReq.hasUpdatedConfig()); + assertFalse(clientReq.hasUpdatedGeneration()); + } + + @Test + public void request_interface_is_implemented() { + JRTClientConfigRequest request = clientReq; + assertFalse(request.containsPayload()); + assertFalse(request.isError()); + assertThat(request.errorCode(), is(clientReq.getRequest().errorCode())); + assertThat(request.errorMessage(), is(clientReq.getRequest().errorMessage())); + assertNotNull(request.getRequest()); + assertFalse(request.validateResponse()); + //assertNull(request.getNewPayload().getData()); + assertThat(request.getTimeout(), is(timeout)); + assertFalse(request.hasUpdatedConfig()); + assertFalse(request.hasUpdatedGeneration()); + } + + @Test + public void created_from_subscription() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + JRTConfigSubscription<SimpletypesConfig> sub = new JRTConfigSubscription<>(new ConfigKey<>(SimpletypesConfig.class, configId), subscriber, new ConfigSet(), new TimingValues()); + JRTClientConfigRequest request = createReq(sub, Trace.createNew(9)); + assertThat(request.getConfigKey().getName(), is(SimpletypesConfig.CONFIG_DEF_NAME)); + JRTServerConfigRequest serverRequest = createReq(request.getRequest()); + assertTrue(serverRequest.validateParameters()); + } + + @Test + public void created_from_existing_subscription() { + System.setProperty("VESPA_CONFIG_PROTOCOL_VERSION", getProtocolVersion()); + MockConnection connection = new MockConnection(new MockConnection.AbstractResponseHandler() { + @Override + protected void createResponse() { + JRTServerConfigRequest serverRequest = createReq(request); + serverRequest.addOkResponse(createPayload(), currentGeneration, configMd5); + } + }); + + ConfigSourceSet src = new ConfigSourceSet(); + ConfigSubscriber subscriber = new GenericConfigSubscriber(Collections.singletonMap(src, JRTConfigRequester.get(connection, new TimingValues()))); + JRTConfigSubscription<SimpletypesConfig> sub = new JRTConfigSubscription<>(new ConfigKey<>(SimpletypesConfig.class, configId), subscriber, src, new TimingValues()); + sub.subscribe(120_0000); + assertTrue(sub.nextConfig(120_0000)); + sub.close(); + JRTClientConfigRequest nextReq = createReq(sub, Trace.createNew()); + SimpletypesConfig config = sub.getConfig(); + assertThat(nextReq.getRequestConfigMd5(), is(config.getConfigMd5())); + assertThat(nextReq.getRequestGeneration(), is(currentGeneration)); + System.setProperty("VESPA_CONFIG_PROTOCOL_VERSION", ""); + } + + protected abstract String getProtocolVersion(); + + @Test + public void created_from_raw() throws IOException { + RawConfig rawConfig = new RawConfig(new ConfigKey<>(defName, configId, defNamespace), defMd5); + long serverTimeout = 100000L; + JRTClientConfigRequest request = createFromRaw(rawConfig, serverTimeout, Trace.createNew(9)); + assertThat(request.getConfigKey().getName(), is(defName)); + JRTServerConfigRequest serverRequest = createReq(request.getRequest()); + assertTrue(serverRequest.validateParameters()); + assertThat(serverRequest.getTimeout(), is(serverTimeout)); + assertThat(serverRequest.getDefContent().asList(), is(rawConfig.getDefContent())); + } + + + @Test + public void parameters_are_validated() throws IOException { + assertTrue(serverReq.validateParameters()); + assertValidationFail(createReq("35#$#!$@#", defNamespace, defMd5, hostname, configId, configMd5, currentGeneration, timeout, trace)); + assertValidationFail(createReq(defName, "abcd.o#$*(!&$", defMd5, hostname, configId, configMd5, currentGeneration, timeout, trace)); + assertValidationFail(createReq(defName, defNamespace, "34", hostname, configId, "34", currentGeneration, timeout, trace)); + assertValidationFail(createReq(defName, defNamespace, defMd5, hostname, configId, "34", currentGeneration, timeout, trace)); + assertValidationFail(createReq(defName, defNamespace, defMd5, hostname, configId, configMd5, -34, timeout, trace)); + assertValidationFail(createReq(defName, defNamespace, defMd5, hostname, configId, configMd5, currentGeneration, -23, trace)); + assertValidationFail(createReq(defName, defNamespace, defMd5, "", configId, configMd5, currentGeneration, timeout, trace)); + } + + private void assertValidationFail(JRTClientConfigRequest req) { + assertFalse(createReq(req.getRequest()).validateParameters()); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestFactoryTest.java b/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestFactoryTest.java new file mode 100644 index 00000000000..ac3a7f16505 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestFactoryTest.java @@ -0,0 +1,143 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.protocol; + +import com.yahoo.foo.FunctionTestConfig; +import com.yahoo.config.subscription.ConfigSet; +import com.yahoo.config.subscription.ConfigSubscriber; +import com.yahoo.config.subscription.impl.JRTConfigSubscription; +import com.yahoo.vespa.config.ConfigKey; +import com.yahoo.vespa.config.RawConfig; +import com.yahoo.vespa.config.TimingValues; +import org.junit.Test; + +import java.util.Optional; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author musum + */ +public class JRTConfigRequestFactoryTest { + private static VespaVersion defaultVespaVersion = JRTConfigRequestFactory.getCompiledVespaVersion(); + + @Test + public void testGetProtocolVersion() { + assertThat(JRTConfigRequestFactory.getProtocolVersion("", "", ""), is("3")); + + assertThat(JRTConfigRequestFactory.getProtocolVersion("1", "", ""), is("1")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("", "1", ""), is("1")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("", "", "1"), is("1")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("1", "1", ""), is("1")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("1", "", "1"), is("1")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("", "1", "1"), is("1")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("1", "1", "1"), is("1")); + + assertThat(JRTConfigRequestFactory.getProtocolVersion("2", "", ""), is("2")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("", "2", ""), is("2")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("", "", "2"), is("2")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("2", "2", ""), is("2")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("2", "", "2"), is("2")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("", "2", "2"), is("2")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("2", "2", "2"), is("2")); + + assertThat(JRTConfigRequestFactory.getProtocolVersion("1", "2", ""), is("1")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("1", "", "2"), is("1")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("", "1", "2"), is("1")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("2", "1", ""), is("2")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("2", "", "1"), is("2")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("", "2", "1"), is("2")); + + assertThat(JRTConfigRequestFactory.getProtocolVersion("1", "2", "2"), is("1")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("1", "1", "2"), is("1")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("1", "2", "1"), is("1")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("2", "1", "1"), is("2")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("2", "1", "2"), is("2")); + assertThat(JRTConfigRequestFactory.getProtocolVersion("2", "2", "1"), is("2")); + } + + @Test + public void testCompressionType() { + assertThat(JRTConfigRequestFactory.getCompressionType("", "", ""), is(CompressionType.LZ4)); + + assertThat(JRTConfigRequestFactory.getCompressionType("UNCOMPRESSED", "", ""), is(CompressionType.UNCOMPRESSED)); + assertThat(JRTConfigRequestFactory.getCompressionType("", "UNCOMPRESSED", ""), is(CompressionType.UNCOMPRESSED)); + assertThat(JRTConfigRequestFactory.getCompressionType("", "", "UNCOMPRESSED"), is(CompressionType.UNCOMPRESSED)); + assertThat(JRTConfigRequestFactory.getCompressionType("UNCOMPRESSED", "UNCOMPRESSED", ""), is(CompressionType.UNCOMPRESSED)); + assertThat(JRTConfigRequestFactory.getCompressionType("UNCOMPRESSED", "", "UNCOMPRESSED"), is(CompressionType.UNCOMPRESSED)); + assertThat(JRTConfigRequestFactory.getCompressionType("", "UNCOMPRESSED", "UNCOMPRESSED"), is(CompressionType.UNCOMPRESSED)); + assertThat(JRTConfigRequestFactory.getCompressionType("UNCOMPRESSED", "UNCOMPRESSED", "UNCOMPRESSED"), is(CompressionType.UNCOMPRESSED)); + + assertThat(JRTConfigRequestFactory.getCompressionType("LZ4", "", ""), is(CompressionType.LZ4)); + assertThat(JRTConfigRequestFactory.getCompressionType("", "LZ4", ""), is(CompressionType.LZ4)); + assertThat(JRTConfigRequestFactory.getCompressionType("", "", "LZ4"), is(CompressionType.LZ4)); + assertThat(JRTConfigRequestFactory.getCompressionType("LZ4", "LZ4", ""), is(CompressionType.LZ4)); + assertThat(JRTConfigRequestFactory.getCompressionType("LZ4", "", "LZ4"), is(CompressionType.LZ4)); + assertThat(JRTConfigRequestFactory.getCompressionType("", "LZ4", "LZ4"), is(CompressionType.LZ4)); + assertThat(JRTConfigRequestFactory.getCompressionType("LZ4", "LZ4", "LZ4"), is(CompressionType.LZ4)); + + assertThat(JRTConfigRequestFactory.getCompressionType("UNCOMPRESSED", "LZ4", ""), is(CompressionType.UNCOMPRESSED)); + assertThat(JRTConfigRequestFactory.getCompressionType("UNCOMPRESSED", "", "LZ4"), is(CompressionType.UNCOMPRESSED)); + assertThat(JRTConfigRequestFactory.getCompressionType("", "UNCOMPRESSED", "LZ4"), is(CompressionType.UNCOMPRESSED)); + assertThat(JRTConfigRequestFactory.getCompressionType("LZ4", "UNCOMPRESSED", ""), is(CompressionType.LZ4)); + assertThat(JRTConfigRequestFactory.getCompressionType("LZ4", "", "UNCOMPRESSED"), is(CompressionType.LZ4)); + assertThat(JRTConfigRequestFactory.getCompressionType("", "LZ4", "UNCOMPRESSED"), is(CompressionType.LZ4)); + + assertThat(JRTConfigRequestFactory.getCompressionType("UNCOMPRESSED", "LZ4", "LZ4"), is(CompressionType.UNCOMPRESSED)); + assertThat(JRTConfigRequestFactory.getCompressionType("UNCOMPRESSED", "UNCOMPRESSED", "LZ4"), is(CompressionType.UNCOMPRESSED)); + assertThat(JRTConfigRequestFactory.getCompressionType("UNCOMPRESSED", "LZ4", "UNCOMPRESSED"), is(CompressionType.UNCOMPRESSED)); + assertThat(JRTConfigRequestFactory.getCompressionType("LZ4", "UNCOMPRESSED", "UNCOMPRESSED"), is(CompressionType.LZ4)); + assertThat(JRTConfigRequestFactory.getCompressionType("LZ4", "UNCOMPRESSED", "LZ4"), is(CompressionType.LZ4)); + assertThat(JRTConfigRequestFactory.getCompressionType("LZ4", "LZ4", "UNCOMPRESSED"), is(CompressionType.LZ4)); + } + + @Test + public void testVespaVersion() { + assertThat(JRTConfigRequestFactory.getVespaVersion().get(), is(defaultVespaVersion)); + } + + @Test + public void testCreateFromSub() { + ConfigSubscriber subscriber = new ConfigSubscriber(); + Class<FunctionTestConfig> clazz = FunctionTestConfig.class; + final String configId = "foo"; + JRTConfigSubscription<FunctionTestConfig> sub = new JRTConfigSubscription<>( + new ConfigKey<>(clazz, configId), subscriber, new ConfigSet(), new TimingValues()); + + // Default vespa version + JRTClientConfigRequest request = JRTConfigRequestFactory.createFromSub(sub); + assertThat(request.getProtocolVersion(), is(3L)); + assertThat(request.getVespaVersion().get(), is(defaultVespaVersion)); + + // Create with vespa version set + String version = "5.37.38"; + System.setProperty(JRTConfigRequestFactory.VESPA_VERSION, version); + request = JRTConfigRequestFactory.createFromSub(sub); + assertThat(request.getProtocolVersion(), is(3L)); + assertThat(request.getVespaVersion().get(), is(VespaVersion.fromString(version))); + + System.clearProperty(JRTConfigRequestFactory.VESPA_VERSION); + } + + @Test + public void testCreateFromRaw() { + Class<FunctionTestConfig> clazz = FunctionTestConfig.class; + final String configId = "foo"; + RawConfig config = new RawConfig(new ConfigKey<>(clazz, configId), "595f44fec1e92a71d3e9e77456ba80d1"); + + // Default vespa version + JRTClientConfigRequest request = JRTConfigRequestFactory.createFromRaw(config, 1000); + assertThat(request.getProtocolVersion(), is(3L)); + assertThat(request.getVespaVersion().get(), is(defaultVespaVersion)); + + // Create with vespa version set + String version = "5.37.38"; + System.setProperty(JRTConfigRequestFactory.VESPA_VERSION, version); + request = JRTConfigRequestFactory.createFromRaw(config, 1000); + assertThat(request.getProtocolVersion(), is(3L)); + assertThat(request.getVespaVersion().get(), is(VespaVersion.fromString(version))); + + System.clearProperty(JRTConfigRequestFactory.VESPA_VERSION); + } + +} diff --git a/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestV3Test.java b/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestV3Test.java new file mode 100644 index 00000000000..bcb88b2ff5e --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/protocol/JRTConfigRequestV3Test.java @@ -0,0 +1,78 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.protocol; + +import com.yahoo.foo.SimpletypesConfig; +import com.yahoo.config.subscription.impl.JRTConfigSubscription; +import com.yahoo.jrt.Request; +import com.yahoo.vespa.config.*; +import com.yahoo.vespa.config.util.ConfigUtils; +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author lulf + * @since 5.19 + */ +public class JRTConfigRequestV3Test extends JRTConfigRequestBase { + + @Override + protected JRTClientConfigRequest createReq(String defName, String defNamespace, String defMd5, String hostname, String configId, String configMd5, long currentGeneration, long timeout, Trace trace) throws IOException { + return JRTClientConfigRequestV3.createWithParams(ConfigKey.createFull(defName, configId, defNamespace, defMd5), + DefContent.fromList(Arrays.asList("namespace=my.name.space", "myfield string")), + hostname, + configMd5, + currentGeneration, + timeout, + trace, + CompressionType.LZ4, + vespaVersion); + } + + @Override + protected JRTServerConfigRequest createReq(Request request) { + return JRTServerConfigRequestV3.createFromRequest(request); + } + + @Override + protected JRTClientConfigRequest createReq(JRTConfigSubscription<SimpletypesConfig> sub, Trace aNew) { + return JRTClientConfigRequestV3.createFromSub(sub, aNew, CompressionType.LZ4, vespaVersion); + } + + @Override + protected JRTClientConfigRequest createFromRaw(RawConfig rawConfig, long serverTimeout, Trace aNew) { + return JRTClientConfigRequestV3.createFromRaw(rawConfig, serverTimeout, aNew, CompressionType.LZ4, vespaVersion); + } + + @Override + protected String getProtocolVersion() { + return "3"; + } + + @Test + public void request_is_parsed() { + request_is_parsed_base(); + assertThat(serverReq.getVespaVersion().toString(), is(vespaVersion.toString())); + } + + @Test + public void next_request_is_correct() { + JRTServerConfigRequest next = next_request_is_correct_base(); + assertThat(next.getVespaVersion().toString(), is(vespaVersion.toString())); + } + + @Test + public void emptypayload() { + ConfigPayload payload = ConfigPayload.empty(); + SlimeConfigResponse response = SlimeConfigResponse.fromConfigPayload(payload, null, 0, ConfigUtils.getMd5(payload)); + serverReq.addOkResponse(serverReq.payloadFromResponse(response), response.getGeneration(), response.getConfigMd5()); + assertTrue(clientReq.validateResponse()); + assertTrue(clientReq.hasUpdatedGeneration()); + assertThat(clientReq.getNewPayload().withCompression(CompressionType.UNCOMPRESSED).getData().toString(), is("{}")); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/protocol/PayloadTest.java b/config/src/test/java/com/yahoo/vespa/config/protocol/PayloadTest.java new file mode 100644 index 00000000000..c3cbfecf1f1 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/protocol/PayloadTest.java @@ -0,0 +1,85 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.protocol; + +import com.google.common.testing.EqualsTester; +import com.yahoo.slime.Slime; +import com.yahoo.text.Utf8Array; +import com.yahoo.vespa.config.ConfigPayload; +import com.yahoo.vespa.config.LZ4PayloadCompressor; +import org.junit.Test; + +import java.io.UnsupportedEncodingException; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.fail; + +/** + * @author lulf + * @since 5.21 + */ +public class PayloadTest { + + @Test + public void testUncompressedToCompressedWithoutCompressionInfo() { + String json = "{\"foo\":13}"; + ConfigPayload configPayload = ConfigPayload.fromString(json); + Payload payload = Payload.from(configPayload); + assertThat(payload.getData().toString(), is(json)); + payload = Payload.from(payload.getData(), CompressionInfo.create(CompressionType.UNCOMPRESSED, 0)); + Payload compressed = payload.withCompression(CompressionType.LZ4); + Payload uncompressed = compressed.withCompression(CompressionType.UNCOMPRESSED); + assertThat(uncompressed.getData().toString(), is(json)); + assertThat(compressed.toString(), is(json)); + assertThat(uncompressed.toString(), is(json)); + } + + @Test + public void testEquals() { + final String foo1 = "foo 1"; + final String foo2 = "foo 2"; + + Payload a = Payload.from(foo1); + Payload b = Payload.from(foo1); + + Payload c = Payload.from(foo2); + + Slime slime = new Slime(); + slime.setString(foo1); + Payload d = Payload.from(new ConfigPayload(slime)); + + slime.setString(foo1); + Payload e = Payload.from(new ConfigPayload(slime)); + + slime.setString("foo 2"); + Payload f = Payload.from(new ConfigPayload(slime)); + + Payload g = null; + Payload h = null; + Payload i = null; + Payload j = null; + try { + g = Payload.from(new Utf8Array(foo1.getBytes("UTF-8")), CompressionInfo.uncompressed()); + h = Payload.from(new Utf8Array(foo1.getBytes("UTF-8")), CompressionInfo.uncompressed()); + + LZ4PayloadCompressor compressor = new LZ4PayloadCompressor(); + CompressionInfo info = CompressionInfo.create(CompressionType.LZ4, foo2.length()); + Utf8Array compressed = new Utf8Array(compressor.compress(foo2.getBytes())); + + i = Payload.from(compressed, info); + j = Payload.from(compressed, info); + } catch (UnsupportedEncodingException e1) { + fail(); + } + + new EqualsTester() + .addEqualityGroup(a, b, g, h) + .addEqualityGroup(c) + .addEqualityGroup(d, e) + .addEqualityGroup(f) + .addEqualityGroup(i, j). + testEquals(); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/protocol/SlimeTraceSerializerTest.java b/config/src/test/java/com/yahoo/vespa/config/protocol/SlimeTraceSerializerTest.java new file mode 100644 index 00000000000..4c69fa3e395 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/protocol/SlimeTraceSerializerTest.java @@ -0,0 +1,123 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.protocol; + +import com.yahoo.slime.JsonFormat; +import com.yahoo.slime.Slime; +import com.yahoo.text.Utf8; +import com.yahoo.yolean.trace.TraceNode; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author lulf + * @since 5.5 + */ +public class SlimeTraceSerializerTest { + @Test + public void test_serializer() throws IOException { + TraceNode root = new TraceNode(null, 1); + root.add(new TraceNode("foo", 4)); + root.add(new TraceNode("bar", 5).add(new TraceNode("baz", 2).add(new TraceNode("quux", 10)))); + assertThat(toJson(root), is("{\"timestamp\":1,\"children\":[{\"timestamp\":5,\"payload\":\"bar\",\"children\":[{\"timestamp\":2,\"payload\":\"baz\",\"children\":[{\"timestamp\":10,\"payload\":\"quux\"}]}]},{\"timestamp\":4,\"payload\":\"foo\"}]}")); + assertSerialize(root); + } + + private String toJson(TraceNode root) throws IOException { + Slime slime = new Slime(); + SlimeTraceSerializer serializer = new SlimeTraceSerializer(slime.setObject()); + root.accept(serializer); + JsonFormat format = new JsonFormat(true); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + format.encode(baos, slime); + return Utf8.toString(baos.toByteArray()); + } + + private void assertSerialize(TraceNode root) { + Slime slime = new Slime(); + SlimeTraceSerializer serializer = new SlimeTraceSerializer(slime.setObject()); + root.accept(serializer); + SlimeTraceDeserializer deserializer = new SlimeTraceDeserializer(slime.get()); + TraceNode deser = deserializer.deserialize(); + assertTraceEqual(deser, root); + } + + private void assertTraceEqual(TraceNode deser, TraceNode root) { + assertThat(deser.timestamp(), is(root.timestamp())); + assertThat(deser.payload(), is(root.payload())); + Iterator<TraceNode> actualIt = deser.children().iterator(); + Iterator<TraceNode> expectedIt = root.children().iterator(); + Map<Long, TraceNode> expectedMapping = new HashMap<>(); + Map<Long, TraceNode> actualMapping = new HashMap<>(); + while (expectedIt.hasNext()) { + assertTrue(actualIt.hasNext()); + TraceNode actualNode = actualIt.next(); + TraceNode expectedNode = expectedIt.next(); + expectedMapping.put(expectedNode.timestamp(), expectedNode); + actualMapping.put(actualNode.timestamp(), actualNode); + } + assertFalse(expectedIt.hasNext()); + assertFalse(actualIt.hasNext()); + for (long timestamp : expectedMapping.keySet()) { + assertTraceEqual(actualMapping.get(timestamp), expectedMapping.get(timestamp)); + } + } + + @Test + public void test_long() throws IOException { + TraceNode root = new TraceNode(14l, 5); + assertThat(toJson(root), is("{\"timestamp\":5,\"payload\":14}")); + assertSerialize(root); + } + + @Test + public void test_double() throws IOException { + TraceNode root = new TraceNode(3.5, 5); + assertThat(toJson(root), is("{\"timestamp\":5,\"payload\":3.5}")); + assertSerialize(root); + } + + @Test + public void test_bool() throws IOException { + TraceNode root = new TraceNode(true, 5); + assertThat(toJson(root), is("{\"timestamp\":5,\"payload\":true}")); + assertSerialize(root); + } + + @Test + public void test_string() throws IOException { + TraceNode root = new TraceNode("bar", 5); + assertThat(toJson(root), is("{\"timestamp\":5,\"payload\":\"bar\"}")); + assertSerialize(root); + } + + @Test + public void test_unknown() throws IOException { + TraceNode root = new TraceNode(new ArrayList<String>(), 5); + assertThat(toJson(root), is("{\"timestamp\":5}")); + } + + @Test + public void test_null() throws IOException { + TraceNode root = new TraceNode(null, 5); + assertThat(toJson(root), is("{\"timestamp\":5}")); + assertSerialize(root); + } + + @Test + public void test_bytearray() throws IOException { + TraceNode root = new TraceNode(new byte[] { 3, 5 }, 5); + assertThat(toJson(root), is("{\"timestamp\":5,\"payload\":\"0x0305\"}")); + assertSerialize(root); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/protocol/TraceTest.java b/config/src/test/java/com/yahoo/vespa/config/protocol/TraceTest.java new file mode 100644 index 00000000000..3e9d0650ae5 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/protocol/TraceTest.java @@ -0,0 +1,61 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.protocol; + +import com.yahoo.slime.Slime; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author lulf + * @since 5.3 + */ +public class TraceTest { + + @Test + public void trace_level_limits_tracing() { + Trace trace = Trace.createNew(3); + assertTrue(trace.shouldTrace(0)); + assertTrue(trace.shouldTrace(1)); + assertTrue(trace.shouldTrace(2)); + assertTrue(trace.shouldTrace(3)); + assertFalse(trace.shouldTrace(4)); + assertFalse(trace.shouldTrace(5)); + trace.trace(1, "foo"); + trace.trace(1, "foo2"); + trace.trace(2, "bar"); + trace.trace(3, "baz"); + trace.trace(3, "baz2"); + trace.trace(4, "quux"); + trace.trace(5, "quux2"); + String str = trace.toString(); + assertTrue(str.contains("foo")); + assertTrue(str.contains("foo2")); + assertTrue(str.contains("bar")); + assertTrue(str.contains("baz")); + assertTrue(str.contains("baz2")); + assertFalse(str.contains("quux")); + assertFalse(str.contains("quux2")); + } + + @Test + public void trace_serialization() { + Trace trace = Trace.createNew(1); + trace.trace(0, "foobar"); + trace.trace(1, "barbaz"); + Slime slime = new Slime(); + trace.serialize(slime.setObject()); + Trace trace2 = Trace.fromSlime(slime.get()); + trace2.trace(1, "quux"); + String trace1Str = trace.toString(); + String trace2Str = trace2.toString(); + assertTrue(trace1Str.contains("foobar")); + assertTrue(trace1Str.contains("barbaz")); + assertFalse(trace1Str.contains("quux")); + + assertTrue(trace2Str.contains("foobar")); + assertTrue(trace2Str.contains("barbaz")); + assertTrue(trace2Str.contains("quux")); + } +} diff --git a/config/src/test/java/com/yahoo/vespa/config/util/ConfigUtilsTest.java b/config/src/test/java/com/yahoo/vespa/config/util/ConfigUtilsTest.java new file mode 100644 index 00000000000..d07c007c13f --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/util/ConfigUtilsTest.java @@ -0,0 +1,288 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.config.util; + +import com.yahoo.collections.Tuple2; +import com.yahoo.foo.SimpletypesConfig; +import com.yahoo.io.IOUtils; +import com.yahoo.vespa.config.ConfigDefinitionKey; +import com.yahoo.vespa.config.ConfigPayload; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +/** + * Tests ConfigUtils. + * + * @author <a href="musum@yahoo-inc.com">Harald Musum</a> + */ +public class ConfigUtilsTest { + + @Test + public void testGetDefMd5() { + String expectedMd5 = "a29038b17c727dabc572a967a508dc1f"; + List<String> lines = new ArrayList<>(); + + // Create normalized lines + lines.add("a"); + lines.add("foo=1 # a comment"); + lines.add("int a default=1 range = [,]"); + lines.add(""); //empty line should not affect md5sum + lines.add("double b default=1.0 range = [,]"); + lines.add("collectiontype enum { SINGLE, ARRAY, WEIGHTEDSET } default=SINGLE"); + + assertThat(ConfigUtils.getDefMd5(lines), is(expectedMd5)); + + lines.clear(); + + // Test various normalizing features implemented by getMd5 + + // Check that lines are trimmed + lines.add("a "); + // Check that trailing comments are trimmed + lines.add("foo=1"); + // Check that upper and lower bounds for int and double ranges are set correctly + lines.add("int a default=1 range = [-2147483648,2147483647]"); + lines.add("double b default=1.0 range = [-100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000," + + "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000]"); + // check that space before commas are treated correctly + lines.add("collectiontype enum { SINGLE , ARRAY , WEIGHTEDSET } default=SINGLE"); + assertThat(ConfigUtils.getDefMd5(lines), is(expectedMd5)); + } + + @Test + public void testGetMd5WithComment() { + String expectedMd5 = "4395db1dfbd977c4d74190d2d23396e2"; + List<String> lines = new ArrayList<>(); + + // Create normalized lines + lines.add("foo=\"1#hello\""); + lines.add(""); //empty line should not affect md5sum + + assertThat(ConfigUtils.getMd5(lines), is(expectedMd5)); + + lines.clear(); + + // Check that comment character in string leads to a different md5 than the original + lines.add("foo=\"1#hello and some more\""); + String md5 = ConfigUtils.getMd5(lines); + assertThat(md5, is(not(expectedMd5))); + + // Check that added characters aft comment character in string leads to a different md5 than above + lines.add("foo=\"1#hello and some more and even more\""); + assertThat(ConfigUtils.getMd5(lines), is(not(md5))); + } + + @Test + public void testGetMd5OfPayload() { + String expectedMd5 = "c9246ed8c8ab55b1c463c501c84075e6"; + String expectedChangedMd5 = "f6f81062ef5f024f1912798490ba7dfc"; + ConfigPayload payload = ConfigPayload.fromInstance(new SimpletypesConfig(new SimpletypesConfig.Builder())); + System.out.println(payload); + assertThat(ConfigUtils.getMd5(payload), is(expectedMd5)); + payload.getSlime().get().setString("fabio", "bar"); + System.out.println(payload); + assertThat(ConfigUtils.getMd5(payload), is(expectedChangedMd5)); + } + + @Test + public void testGetMd5OfString() { + String expectedMd5 = "c9246ed8c8ab55b1c463c501c84075e6"; + String expectedChangedMd5 = "f6f81062ef5f024f1912798490ba7dfc"; + ConfigPayload payload = ConfigPayload.fromInstance(new SimpletypesConfig(new SimpletypesConfig.Builder())); + System.out.println(payload); + assertThat(ConfigUtils.getMd5(payload.toString(true)), is(expectedMd5)); + payload.getSlime().get().setString("fabio", "bar"); + System.out.println(payload); + assertThat(ConfigUtils.getMd5(payload.toString(true)), is(expectedChangedMd5)); + } + + @Test + public void testStripSpaces() { + assertThat(ConfigUtils.stripSpaces("a b"), is("a b")); + assertThat(ConfigUtils.stripSpaces("\"a b\""), is("\"a b\"")); + assertThat(ConfigUtils.stripSpaces("a b \"a b\""), is("a b \"a b\"")); + assertThat(ConfigUtils.stripSpaces("a b"), is("a b")); + } + + @Test + public void testGetVersion() { + StringReader reader = new StringReader("version=1\nint a default=0"); + assertThat(ConfigUtils.getDefVersion(reader), is("1")); + + // no version + reader = new StringReader("int a default=0"); + assertThat(ConfigUtils.getDefVersion(reader), is("")); + + // namespace and version + reader = new StringReader("version=1\nnamespace=foo\nint a default=0"); + assertThat(ConfigUtils.getDefVersion(reader), is("1")); + reader = new StringReader("namespace=foo\nversion=1\nint a default=0"); + assertThat(ConfigUtils.getDefVersion(reader), is("1")); + } + + @Test + public void testGetNamespace() { + StringReader reader = new StringReader("version=1\nnamespace=a\nint a default=0"); + assertThat(ConfigUtils.getDefNamespace(reader), is("a")); + // namespace first + reader = new StringReader("namespace=a\nversion=1\nint a default=0"); + assertThat(ConfigUtils.getDefNamespace(reader), is("a")); + + // No namespace + reader = new StringReader("version=1\nint a default=0"); + assertThat(ConfigUtils.getDefNamespace(reader), is("")); + + // comment lines + reader = new StringReader("#comment\nversion=1\n#comment2\nint a default=0"); + assertThat(ConfigUtils.getDefNamespace(reader), is("")); + + try { + ConfigUtils.getDefNamespace(null); + fail(); + } catch (IllegalArgumentException e) { + // + } + } + + @Test + public void testGetNameCommaVersion() { + String nameCommaversion = "foo,1"; + Tuple2<String, String> tuple = ConfigUtils.getNameAndVersionFromString(nameCommaversion); + assertThat(tuple.first, is("foo")); + assertThat(tuple.second, is("1")); + + // no version + nameCommaversion = "foo"; + tuple = ConfigUtils.getNameAndVersionFromString(nameCommaversion); + assertThat(tuple.first, is("foo")); + assertThat(tuple.second, is("")); + + // no name + nameCommaversion = ",1"; + tuple = ConfigUtils.getNameAndVersionFromString(nameCommaversion); + assertThat(tuple.first, is("")); + assertThat(tuple.second, is("1")); + } + + @Test + public void testNamespaceDotNames() { + String namespaceDotName = "foo.bar"; + Tuple2<String, String> tuple = ConfigUtils.getNameAndNamespaceFromString(namespaceDotName); + assertThat(tuple.first, is("bar")); + assertThat(tuple.second, is("foo")); + + namespaceDotName = "foo.baz.bar"; + tuple = ConfigUtils.getNameAndNamespaceFromString(namespaceDotName); + assertThat(tuple.first, is("bar")); + assertThat(tuple.second, is("foo.baz")); + + // no namespace + namespaceDotName = "bar"; + tuple = ConfigUtils.getNameAndNamespaceFromString(namespaceDotName); + assertThat(tuple.first, is("bar")); + assertThat(tuple.second, is("")); + + // no name + namespaceDotName = "foo."; + tuple = ConfigUtils.getNameAndNamespaceFromString(namespaceDotName); + assertThat(tuple.first, is("")); + assertThat(tuple.second, is("foo")); + + // no namespace + namespaceDotName = ".bar"; + tuple = ConfigUtils.getNameAndNamespaceFromString(namespaceDotName); + assertThat(tuple.first, is("bar")); + assertThat(tuple.second, is("")); + } + + @Test + public void testGetConfigDefinitionKey() { + String input = "foo"; + ConfigDefinitionKey def = ConfigUtils.getConfigDefinitionKeyFromString(input); + assertThat(def.getName(), is("foo")); + assertThat(def.getNamespace(), is("")); + + input = "foo.bar"; + def = ConfigUtils.getConfigDefinitionKeyFromString(input); + assertThat(def.getName(), is("bar")); + assertThat(def.getNamespace(), is("foo")); + + input = "foo.bar.1"; + def = ConfigUtils.getConfigDefinitionKeyFromString(input); + assertThat(def.getName(), is("bar")); + assertThat(def.getNamespace(), is("foo")); + + input = "foo.bar.qux.2"; + def = ConfigUtils.getConfigDefinitionKeyFromString(input); + assertThat(def.getName(), is("qux")); + assertThat(def.getNamespace(), is("foo.bar")); + + input = "foo.2"; + def = ConfigUtils.getConfigDefinitionKeyFromString(input); + assertThat(def.getName(), is("foo")); + assertThat(def.getNamespace(), is("")); + } + + @Test + public void testCreateConfigDefinitionKeyFromZKString() { + String input = "foo,1"; + ConfigDefinitionKey def = ConfigUtils.createConfigDefinitionKeyFromZKString(input); + assertThat(def.getName(), is("foo")); + assertThat(def.getNamespace(), is("")); + + input = "bar.foo,1"; + def = ConfigUtils.createConfigDefinitionKeyFromZKString(input); + assertThat(def.getName(), is("foo")); + assertThat(def.getNamespace(), is("bar")); + } + + @Test + public void testCreateConfigDefinitionKeyFromDefFile() { + ConfigDefinitionKey def = null; + try { + def = ConfigUtils.createConfigDefinitionKeyFromDefFile(new File("src/test/resources/configs/def-files/app.def")); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + assertThat(def.getName(), is("app")); + assertThat(def.getNamespace(), is("foo")); + + try { + def = ConfigUtils.createConfigDefinitionKeyFromDefFile(new File("src/test/resources/configs/def-files/testnamespace.def")); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + assertThat(def.getName(), is("testnamespace")); + assertThat(def.getNamespace(), is("foo")); + + try { + byte[] content = IOUtils.readFileBytes(new File("src/test/resources/configs/def-files/app.def")); + def = ConfigUtils.createConfigDefinitionKeyFromDefContent("app", content); + } catch (IOException e) { + fail(); + } + assertThat(def.getName(), is("app")); + assertThat(def.getNamespace(), is("foo")); + + try { + byte[] content = IOUtils.readFileBytes(new File("src/test/resources/configs/def-files-nogen/foo.bar.app.def")); + def = ConfigUtils.createConfigDefinitionKeyFromDefContent("app", content); + } catch (IOException e) { + fail(); + } + assertThat(def.getName(), is("app")); + assertThat(def.getNamespace(), is("mynamespace")); + } + +} diff --git a/config/src/test/java/com/yahoo/vespa/config/xml/whitespace-test.xml b/config/src/test/java/com/yahoo/vespa/config/xml/whitespace-test.xml new file mode 100644 index 00000000000..6f63e405df8 --- /dev/null +++ b/config/src/test/java/com/yahoo/vespa/config/xml/whitespace-test.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<config name="string"> + <stringVal> This is a string + that contains different kinds of whitespace </stringVal> +</config> diff --git a/config/src/test/resources/configdefinitions/bar.def b/config/src/test/resources/configdefinitions/bar.def new file mode 100644 index 00000000000..9293df5afd5 --- /dev/null +++ b/config/src/test/resources/configdefinitions/bar.def @@ -0,0 +1,4 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=config + +barValue string default="defaultBar" diff --git a/config/src/test/resources/configdefinitions/baz.def b/config/src/test/resources/configdefinitions/baz.def new file mode 100644 index 00000000000..a505df39830 --- /dev/null +++ b/config/src/test/resources/configdefinitions/baz.def @@ -0,0 +1,4 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=config + +bazValue string default="defaultBaz" diff --git a/config/src/test/resources/configdefinitions/bootstrap.def b/config/src/test/resources/configdefinitions/bootstrap.def new file mode 100644 index 00000000000..8cd61352a09 --- /dev/null +++ b/config/src/test/resources/configdefinitions/bootstrap.def @@ -0,0 +1,6 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=config + +component[].name string +component[].configid string + diff --git a/config/src/test/resources/configdefinitions/foo.def b/config/src/test/resources/configdefinitions/foo.def new file mode 100644 index 00000000000..ace1ed3676d --- /dev/null +++ b/config/src/test/resources/configdefinitions/foo.def @@ -0,0 +1,7 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=config + +fooValue string +fooArray[] int +fooStruct[].innerStruct[].bar int +fooMap{} int diff --git a/config/src/test/resources/configdefinitions/foobar.def b/config/src/test/resources/configdefinitions/foobar.def new file mode 100644 index 00000000000..427abfa8d5d --- /dev/null +++ b/config/src/test/resources/configdefinitions/foobar.def @@ -0,0 +1,4 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=config + +fooBarValue string default="defaultFooBar" diff --git a/config/src/test/resources/configdefinitions/foodefault.def b/config/src/test/resources/configdefinitions/foodefault.def new file mode 100644 index 00000000000..06d84a092cf --- /dev/null +++ b/config/src/test/resources/configdefinitions/foodefault.def @@ -0,0 +1,4 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=config + +fooValue string default = "per" diff --git a/config/src/test/resources/configdefinitions/function-test.def b/config/src/test/resources/configdefinitions/function-test.def new file mode 100644 index 00000000000..40047418eb0 --- /dev/null +++ b/config/src/test/resources/configdefinitions/function-test.def @@ -0,0 +1,74 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# +# This def file should test most aspects of def files that makes a difference +# for the autogenerated config classes. The goal is to trigger all blocks of +# code in the code generators. This includes: +# +# - Use all legal special characters in the def file name, to ensure that those +# that needs to be replaced in type names are actually replaced. +# - Use the same enum type twice to verify that we dont declare or define it +# twice. +# - Use the same struct type twice for the same reason. +# - Include arrays of primitives and structs. +# - Include enum primitives and array of enums. Arrays of enums must be handled +# specially by the C++ code. +# - Include enums both with and without default values. +# - Include primitive string, numbers & doubles both with and without default +# values. +# - Have an array within a struct, to verify that we correctly recurse. +# - Reuse type name further within to ensure that this works. + +namespace=config + +# Some random bool without a default value. These comments exist to check + # that comment parsing works. +bool_val bool + ## A bool with a default value set. +bool_with_def bool default=false +int_val int +int_with_def int default=-545 +long_val long +long_with_def long default=-50000000000 +double_val double +double_with_def double default=-6.43 +# Another comment +string_val string +stringwithdef string default="foobar" +enum_val enum { FOO, BAR, FOOBAR } +enumwithdef enum { FOO2, BAR2, FOOBAR2 } default=BAR2 +onechoice enum { ONLYFOO } default=ONLYFOO +refval reference +refwithdef reference default=":parent:" +fileVal file + +boolarr[] bool +intarr[] int +longarr[] long +doublearr[] double +stringarr[] string +enumarr[] enum { ARRAY, VALUES } +refarr[] reference +fileArr[] file + +# A basic struct +basicStruct.foo string default="basic" +basicStruct.bar int +basicStruct.intArr[] int + +# A struct of struct +rootStruct.inner0.name string default="inner0" +rootStruct.inner0.index int +rootStruct.inner1.name string default="inner1" +rootStruct.inner1.index int +rootStruct.inner2.array[] int +rootStruct.innerArr[].boolVal bool default=false +rootStruct.innerArr[].stringVal string + +myarray[].intval int default=14 +myarray[].stringval[] string +myarray[].enumval enum { INNER, ENUM, TYPE } default=TYPE +myarray[].refval reference # Value in array without default +myarray[].fileVal file +myarray[].anotherarray[].foo int default=-4 +myarray[].myStruct.a int +myarray[].myStruct.b int default=2 diff --git a/config/src/test/resources/configdefinitions/motd.def b/config/src/test/resources/configdefinitions/motd.def new file mode 100644 index 00000000000..554ffa8fb28 --- /dev/null +++ b/config/src/test/resources/configdefinitions/motd.def @@ -0,0 +1,84 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# +# This def file should test most aspects of def files that makes a difference +# for the autogenerated config classes. The goal is to trigger all blocks of +# code in the code generators. This includes: +# +# - Use all legal special characters in the def file name, to ensure that those +# that needs to be replaced in type names are actually replaced. +# - Use the same enum type twice to verify that we dont declare or define it +# twice. +# - Use the same struct type twice for the same reason. +# - Include arrays of primitives and structs. +# - Include enum primitives and array of enums. Arrays of enums must be handled +# specially by the C++ code. +# - Include enums both with and without default values. +# - Include primitive string, numbers & doubles both with and without default +# values. +# - Have an array within a struct, to verify that we correctly recurse. +# - Reuse type name further within to ensure that this works. + +namespace=config + +# Some random bool without a default value. These comments exist to check + # that comment parsing works.e +boolVal bool + ## A bool with a default value set. +bool_with_def bool default=false +intVal int +intWithDef int default=-545 +longVal long +longWithDef long default=1234567890123 +doubleVal double +double_with_def double default=-6.43 +# Another comment +stringVal string +stringwithdef string default="foobar" +stringnulldef string default="null" +enumVal enum { FOO, BAR, FOOBAR } +enumwithdef enum { FOO2, BAR2, FOOBAR2 } default=BAR2 +refVal reference +refwithdef reference default=":parent:" +fileVal file + +boolarr[] bool +intarr[] int +longarr[] long +doublearr[] double +stringarr[] string +enumarr[] enum { ARRAY, VALUES } +refarr[] reference +filearr[] file + +boolmap{} bool +intmap{} int +longmap{} long +doublemap{} double +stringmap{} string +enummap{} enum { LOL1, LOL2 } +refmap{} reference +filemap{} file + +structmap{}.foo int +mapmap{}.map{}.bar int + +# A basic struct +basic_struct.foo string default="foo" +basic_struct.bar int default=0 + +# A struct of struct +struct_of_struct.inner0.name string default="inner0" +struct_of_struct.inner0.index int default=0 +struct_of_struct.inner1.name string default="inner1" +struct_of_struct.inner1.index int default=1 + +myArray[].intVal int default=14 +myArray[].stringVal[] string +myArray[].enumVal enum { INNER, ENUM, TYPE } default=TYPE +myArray[].refVal reference # Value in array without default +myArray[].anotherArray[].foo int default=-4 + +value string default="value" +buffer int default=-1 +rhs string default="rhs" +lines string default="lines" diff --git a/config/src/test/resources/configdefinitions/my.def b/config/src/test/resources/configdefinitions/my.def new file mode 100644 index 00000000000..8c6728fc63c --- /dev/null +++ b/config/src/test/resources/configdefinitions/my.def @@ -0,0 +1,4 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=config + +myField string diff --git a/config/src/test/resources/configs/bar/app.cfg b/config/src/test/resources/configs/bar/app.cfg new file mode 100644 index 00000000000..8e4096805ea --- /dev/null +++ b/config/src/test/resources/configs/bar/app.cfg @@ -0,0 +1,7 @@ +message "msg2" +times 2 +a[4] +a[0].name "a0" +a[1].name "a1" +a[2].name "a2" +a[3].name "a3" diff --git a/config/src/test/resources/configs/bar/string.cfg b/config/src/test/resources/configs/bar/string.cfg new file mode 100644 index 00000000000..5cb1a496e0e --- /dev/null +++ b/config/src/test/resources/configs/bar/string.cfg @@ -0,0 +1 @@ +stringVal "My mess" diff --git a/config/src/test/resources/configs/baz/app.1.cfg.new b/config/src/test/resources/configs/baz/app.1.cfg.new new file mode 100644 index 00000000000..b734dd17297 --- /dev/null +++ b/config/src/test/resources/configs/baz/app.1.cfg.new @@ -0,0 +1,85 @@ +times 3 +a[0]=1 +a[2]=1 +a[3]=1 +a[4]=1 +a[5]=1 +a[6]=1 +a[7]=1 +a[8]=1 +a[9]=1 +a[10]=1 +a[11]=1 +a[12]=1 +a[13]=1 +a[14]=1 +a[15]=1 +a[16]=1 +a[17]=1 +a[18]=1 +a[19]=1 +a[20]=1 +a[21]=1 +a[22]=1 +a[23]=1 +a[24]=1 +a[25]=1 +a[26]=1 +a[27]=1 +a[28]=1 +a[29]=1 +a[30]=1 +a[31]=1 +a[32]=1 +a[33]=1 +a[34]=1 +a[35]=1 +a[36]=1 +a[37]=1 +a[38]=1 +a[39]=1 +a[40]=1 +a[41]=1 +a[42]=1 +a[43]=1 +a[44]=1 +a[45]=1 +a[46]=1 +a[47]=1 +a[48]=1 +a[49]=1 +a[50]=1 +a[51]=1 +a[52]=1 +a[53]=1 +a[54]=1 +a[55]=1 +a[56]=1 +a[57]=1 +a[58]=1 +a[59]=1 +a[60]=1 +a[61]=1 +a[62]=1 +a[63]=1 +a[64]=1 +a[65]=1 +a[66]=1 +a[67]=1 +a[68]=1 +a[69]=1 +version 1 +version 1 +version 1 +version 1 +version 1 +version 1 +version 1 +version 1 +version 1 +version 1 +version 1 +version 1 +version 1 +version 1 +version 2 diff --git a/config/src/test/resources/configs/baz/app.cfg b/config/src/test/resources/configs/baz/app.cfg new file mode 100644 index 00000000000..b04f16fe7c9 --- /dev/null +++ b/config/src/test/resources/configs/baz/app.cfg @@ -0,0 +1,5 @@ +times 3 +a[3] +a[0].name "a0" +a[1].name "a1" +a[2].name "a2" diff --git a/config/src/test/resources/configs/datastructures/config1.txt b/config/src/test/resources/configs/datastructures/config1.txt new file mode 100644 index 00000000000..f6121cfa486 --- /dev/null +++ b/config/src/test/resources/configs/datastructures/config1.txt @@ -0,0 +1,17 @@ +date[3] +date[0] 14-Apr-08 +date[1] 15-Apr-08 +date[2] 16-Apr-08 +stock[2] +stock[0].ticker YHOO +stock[0].type COMMON +stock[0].volume[3] +stock[0].volume[0] 1333000 +stock[0].volume[1] 996000 +stock[0].volume[2] 2400000 +stock[1].ticker EWJ +stock[1].type ETF +stock[1].volume[3] +stock[1].volume[0] 4600000 +stock[1].volume[1] 3002000 +stock[1].volume[2] 10987000 diff --git a/config/src/test/resources/configs/datastructures/config1_1.txt b/config/src/test/resources/configs/datastructures/config1_1.txt new file mode 100644 index 00000000000..33a6d6c22aa --- /dev/null +++ b/config/src/test/resources/configs/datastructures/config1_1.txt @@ -0,0 +1,14 @@ +date[2] +date[0] 16-Apr-08 +date[1] 17-Apr-08 +stock[2] +stock[0].ticker YHOO +stock[0].type COMMON +stock[0].volume[2] +stock[0].volume[0] 1333000 +stock[0].volume[1] 800800 +stock[1].ticker JNJ +stock[1].type COMMON +stock[1].volume[2] +stock[1].volume[0] 10987000 +stock[1].volume[1] 1300000 diff --git a/config/src/test/resources/configs/datastructures/config2.txt b/config/src/test/resources/configs/datastructures/config2.txt new file mode 100644 index 00000000000..6cc31add025 --- /dev/null +++ b/config/src/test/resources/configs/datastructures/config2.txt @@ -0,0 +1,19 @@ +date[3] +date[0] 14-Apr-08 +date[1] 15-Apr-08 +date[2] 16-Apr-08 +stock[2] +stock[0].ticker YHOO +stock[0].type COMMON +stock[0].volume[3] +stock[0].volume[0] 1333000 +stock[0].volume[1] 996000 +stock[0].volume[2] 2400000 +stock[1].ticker EWJ +stock[1].type ETF +stock[1].volume[3] +stock[1].volume[0] 4600000 +stock[1].volume[1] 3002000 +stock[1].volume[2] 10987000 +basicstruct.foo "bar" +basicstruct.bar 42 diff --git a/config/src/test/resources/configs/def-files-nogen/foo.bar.app.def b/config/src/test/resources/configs/def-files-nogen/foo.bar.app.def new file mode 100644 index 00000000000..d2a1df70e7a --- /dev/null +++ b/config/src/test/resources/configs/def-files-nogen/foo.bar.app.def @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=mynamespace + +message string default="Hello!" + +times int default=1 + +a[].name string diff --git a/config/src/test/resources/configs/def-files/app.def b/config/src/test/resources/configs/def-files/app.def new file mode 100644 index 00000000000..30919c866e7 --- /dev/null +++ b/config/src/test/resources/configs/def-files/app.def @@ -0,0 +1,9 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=foo +version=1 + +message string default="Hello!" + +times int default=1 + +a[].name string diff --git a/config/src/test/resources/configs/def-files/arraytypes.def b/config/src/test/resources/configs/def-files/arraytypes.def new file mode 100644 index 00000000000..b6e206ff925 --- /dev/null +++ b/config/src/test/resources/configs/def-files/arraytypes.def @@ -0,0 +1,12 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# Config containing only simple array types that can be used for testing +# individual types in detail. +namespace=foo +version=1 + +boolarr[] bool +doublearr[] double +enumarr[] enum { VAL1, VAL2 } +intarr[] int +longarr[] long +stringarr[] string diff --git a/config/src/test/resources/configs/def-files/chains-test.def b/config/src/test/resources/configs/def-files/chains-test.def new file mode 100644 index 00000000000..26a23ff17da --- /dev/null +++ b/config/src/test/resources/configs/def-files/chains-test.def @@ -0,0 +1,43 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# Chains configuration +namespace=foo +version=12 + +component[].id string + +# Configured functionality provided by this - comes in addition to those set in the code +component[].dependencies.provides[] string + +# Configured "before" dependencies provided by this - comes in addition to those set in the code +component[].dependencies.before[] string + +# Configured "after" dependencies provided by this - comes in addition to those set in the code +component[].dependencies.after[] string + +# The id of this chain. The id has the form name(:version)? +# where the version has the form 1(.2(.3(.identifier)?)?)?. +# The default chain must be called "default". +chain[].id string + +#The type of this chain +chain[].type enum {DOCPROC, SEARCH} default=SEARCH + +# The id of a component to include in this chain. +# The id has the form fullclassname(:version)? +# where the version has the form 1(.2(.3(.identifier)?)?)?. +chain[].component[] string + +# The optional list of chain ids this inherits. +# The ids has the form name(:version)? +# where the version has the form 1(.2(.3(.identifier)?)?)?. +# If the version is not specified the newest version is used. +chain[].inherit[] string + +# The optional list of component ids to exclude from this chain even if they exists in inherited chains +# If versions are specified in these ids, they are ignored. +chain[].exclude[] string + +# The phases for a chain +chain[].phase[].id string +chain[].phase[].before[] string +chain[].phase[].after[] string diff --git a/config/src/test/resources/configs/def-files/datastructures.def b/config/src/test/resources/configs/def-files/datastructures.def new file mode 100644 index 00000000000..803931c91bc --- /dev/null +++ b/config/src/test/resources/configs/def-files/datastructures.def @@ -0,0 +1,12 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=foo +version=4 + +date[] string + +stock[].ticker string +stock[].type enum { COMMON, ETF, ETC } default=COMMON +stock[].volume[] int + +basicstruct.foo string default="foo" +basicstruct.bar int default=0 diff --git a/config/src/test/resources/configs/def-files/defaulttest.def b/config/src/test/resources/configs/def-files/defaulttest.def new file mode 100644 index 00000000000..3934a0e4d12 --- /dev/null +++ b/config/src/test/resources/configs/def-files/defaulttest.def @@ -0,0 +1,9 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=foo +version=3 + +nondefaultstring string +defaultstring string default="thedefault" + +nondefaultreference reference +defaultreference reference default="thedefault" diff --git a/config/src/test/resources/configs/def-files/function-test.def b/config/src/test/resources/configs/def-files/function-test.def new file mode 100644 index 00000000000..4529630c6b1 --- /dev/null +++ b/config/src/test/resources/configs/def-files/function-test.def @@ -0,0 +1,89 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# +# This def file should test most aspects of def files that makes a difference +# for the autogenerated config classes. The goal is to trigger all blocks of +# code in the code generators. This includes: +# +# - Use all legal special characters in the def file name, to ensure that those +# that needs to be replaced in type names are actually replaced. +# - Use the same enum type twice to verify that we dont declare or define it +# twice. +# - Use the same struct type twice for the same reason. +# - Include arrays of primitives and structs. +# - Include enum primitives and array of enums. Arrays of enums must be handled +# specially by the C++ code. +# - Include enums both with and without default values. +# - Include primitive string, numbers & doubles both with and without default +# values. +# - Have an array within a struct, to verify that we correctly recurse. +# - Reuse type name further within to ensure that this works. + +version=8 + +namespace=foo + +# Some random bool without a default value. These comments exist to check + # that comment parsing works. +bool_val bool + ## A bool with a default value set. +bool_with_def bool default=false +int_val int +int_with_def int default=-545 +long_val long +long_with_def long default=-50000000000 +double_val double +double_with_def double default=-6.43 +# Another comment +string_val string +stringwithdef string default="foobar" +enum_val enum { FOO, BAR, FOOBAR } +enumwithdef enum { FOO2, BAR2, FOOBAR2 } default=BAR2 +onechoice enum { ONLYFOO } default=ONLYFOO +refval reference +refwithdef reference default=":parent:" +fileVal file +pathVal path + +boolarr[] bool +intarr[] int +longarr[] long +doublearr[] double +stringarr[] string +enumarr[] enum { ARRAY, VALUES } +refarr[] reference +fileArr[] file +pathArr[] path + +intMap{} int +stringMap{} string +filemap{} file +pathMap{} path + +# A basic struct +basicStruct.foo string default="basic" +basicStruct.bar int +basicStruct.intArr[] int + +# A struct of struct +rootStruct.inner0.name string default="inner0" +rootStruct.inner0.index int +rootStruct.inner1.name string default="inner1" +rootStruct.inner1.index int +rootStruct.innerArr[].boolVal bool default=false +rootStruct.innerArr[].stringVal string + +myarray[].intval int default=14 +myarray[].stringval[] string +myarray[].enumval enum { INNER, ENUM, TYPE } default=TYPE +myarray[].refval reference # Value in array without default +myarray[].fileVal file +myarray[].anotherarray[].foo int default=-4 +myarray[].myStruct.a int +myarray[].myStruct.b int default=2 + +myStructMap{}.myInt int +myStructMap{}.myString string +myStructMap{}.myIntDef int default=56 +myStructMap{}.myStringDef string default="g" +myStructMap{}.anotherMap{}.anInt int +myStructMap{}.anotherMap{}.anIntDef int default=11 diff --git a/config/src/test/resources/configs/def-files/int.def b/config/src/test/resources/configs/def-files/int.def new file mode 100755 index 00000000000..fd07c90dfd5 --- /dev/null +++ b/config/src/test/resources/configs/def-files/int.def @@ -0,0 +1,5 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=foo +version=1 + +intVal int default=1 diff --git a/config/src/test/resources/configs/def-files/maptypes.def b/config/src/test/resources/configs/def-files/maptypes.def new file mode 100644 index 00000000000..389a9b71012 --- /dev/null +++ b/config/src/test/resources/configs/def-files/maptypes.def @@ -0,0 +1,13 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# Config containing only structs in various forms +namespace=foo + +boolmap{} bool +intmap{} int +longmap{} long +doublemap{} double +stringmap{} string +filemap{} file + +innermap{}.foo int +nestedmap{}.inner{} int diff --git a/config/src/test/resources/configs/def-files/md5test.def b/config/src/test/resources/configs/def-files/md5test.def new file mode 100644 index 00000000000..f9fd7a645d2 --- /dev/null +++ b/config/src/test/resources/configs/def-files/md5test.def @@ -0,0 +1,27 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=foo +# version=4 , version in comment does not count. + +# Added empty line to see if we can confuse +# the server's md5 calculation +version=3 + +#even adding a variable name starting with 'version' +versiontag int default=3 + +blabla string default="" +tabs string default=" " +test int + +# test multiple spaces/tabs +spaces int +singletab string +multitabs double + +# test enum +normal enum { VAL1, VAL2 } default=VAL1 +spacevalues enum { V1 , V2 , V3 , V4 } default=V3 + +# Comments and empty lines at the end + + diff --git a/config/src/test/resources/configs/def-files/namespace.def b/config/src/test/resources/configs/def-files/namespace.def new file mode 100644 index 00000000000..429b835a6aa --- /dev/null +++ b/config/src/test/resources/configs/def-files/namespace.def @@ -0,0 +1,6 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +version=1 + +namespace=myproject.config + +a int diff --git a/config/src/test/resources/configs/def-files/simpletypes.def b/config/src/test/resources/configs/def-files/simpletypes.def new file mode 100644 index 00000000000..e3954a92edc --- /dev/null +++ b/config/src/test/resources/configs/def-files/simpletypes.def @@ -0,0 +1,12 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=foo +# Config containing only simple leaf types with default values, that can be used +# for testing individual types in detail. +version=1 + +boolval bool default=false +doubleval double default=0.0 +enumval enum { VAL1, VAL2 } default=VAL1 +intval int default=0 +longval long default=0 +stringval string default="s" diff --git a/config/src/test/resources/configs/def-files/specialtypes.def b/config/src/test/resources/configs/def-files/specialtypes.def new file mode 100644 index 00000000000..7b9b68d38a6 --- /dev/null +++ b/config/src/test/resources/configs/def-files/specialtypes.def @@ -0,0 +1,4 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=foo +myfile file +myref reference diff --git a/config/src/test/resources/configs/def-files/standard.def b/config/src/test/resources/configs/def-files/standard.def new file mode 100644 index 00000000000..94e2e3133f4 --- /dev/null +++ b/config/src/test/resources/configs/def-files/standard.def @@ -0,0 +1,9 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# Config containing only simple leaf types with default values, that can be used +# for testing individual types in detail. +namespace=foo +version=1 + +basicStruct.intVal int default=0 +basicStruct.stringVal string default="s" +stringArr[] string diff --git a/config/src/test/resources/configs/def-files/string.def b/config/src/test/resources/configs/def-files/string.def new file mode 100755 index 00000000000..0605dc6e10c --- /dev/null +++ b/config/src/test/resources/configs/def-files/string.def @@ -0,0 +1,5 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=foo +version=1 + +stringVal string default="_default_" diff --git a/config/src/test/resources/configs/def-files/structtypes.def b/config/src/test/resources/configs/def-files/structtypes.def new file mode 100644 index 00000000000..dfcec942799 --- /dev/null +++ b/config/src/test/resources/configs/def-files/structtypes.def @@ -0,0 +1,22 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# Config containing only structs in various forms +namespace=foo +version=2 + +simple.name string default="_default_" +simple.gender enum { MALE, FEMALE } default=MALE +simple.emails[] string + +nested.inner.name string default="_default_" +nested.inner.gender enum { MALE, FEMALE } default=MALE +nested.inner.emails[] string + +simplearr[].name string +simplearr[].gender enum { MALE, FEMALE } + +nestedarr[].inner.name string +nestedarr[].inner.gender enum { MALE, FEMALE } +nestedarr[].inner.emails[] string + +complexarr[].innerarr[].name string +complexarr[].innerarr[].gender enum { MALE, FEMALE } diff --git a/config/src/test/resources/configs/def-files/test-nodefs.def b/config/src/test/resources/configs/def-files/test-nodefs.def new file mode 100644 index 00000000000..e35438a26ff --- /dev/null +++ b/config/src/test/resources/configs/def-files/test-nodefs.def @@ -0,0 +1,17 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +version=2 +namespace=foo + +# test config vars with no defaults + +s string +j int +b bool +f double +e enum { AA, BB, CC } + +basicstruct.foo string +basicstruct.bar int + +arr[].s string +arr[].i int diff --git a/config/src/test/resources/configs/def-files/test-nonstring.def b/config/src/test/resources/configs/def-files/test-nonstring.def new file mode 100644 index 00000000000..4819b32e1ba --- /dev/null +++ b/config/src/test/resources/configs/def-files/test-nonstring.def @@ -0,0 +1,10 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +version=1 +namespace=foo + +# Test non-string config vars with defaults + +i int default=0 +b bool default=false +d double default=0.0 +e enum { AA, BB, CC } default=AA diff --git a/config/src/test/resources/configs/def-files/test-reference.def b/config/src/test/resources/configs/def-files/test-reference.def new file mode 100644 index 00000000000..dacb9a18544 --- /dev/null +++ b/config/src/test/resources/configs/def-files/test-reference.def @@ -0,0 +1,5 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +version=1 +namespace=foo + +configId reference default=":parent:" diff --git a/config/src/test/resources/configs/def-files/testnamespace.def b/config/src/test/resources/configs/def-files/testnamespace.def new file mode 100644 index 00000000000..56710807954 --- /dev/null +++ b/config/src/test/resources/configs/def-files/testnamespace.def @@ -0,0 +1,4 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +version=1 +namespace=foo +basicStruct.stringVal string diff --git a/config/src/test/resources/configs/def-files/unicode.def b/config/src/test/resources/configs/def-files/unicode.def new file mode 100644 index 00000000000..310564f4cfd --- /dev/null +++ b/config/src/test/resources/configs/def-files/unicode.def @@ -0,0 +1,6 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +version=2 +namespace=foo + +unicodestring1 string +unicodestring2 string default="abc æøå 囲碁 ÆØÅ ABC" diff --git a/config/src/test/resources/configs/foo/app.cfg b/config/src/test/resources/configs/foo/app.cfg new file mode 100644 index 00000000000..30426957db6 --- /dev/null +++ b/config/src/test/resources/configs/foo/app.cfg @@ -0,0 +1,6 @@ +message "msg1" +times 3 +a[3] +a[0].name "a0" +a[1].name "a1" +a[2].name "a2" diff --git a/config/src/test/resources/configs/foo/test-reference.cfg b/config/src/test/resources/configs/foo/test-reference.cfg new file mode 100644 index 00000000000..127d63f27dc --- /dev/null +++ b/config/src/test/resources/configs/foo/test-reference.cfg @@ -0,0 +1 @@ +configId ":parent:" diff --git a/config/src/test/resources/configs/function-test/defaultvalues.txt b/config/src/test/resources/configs/function-test/defaultvalues.txt new file mode 100644 index 00000000000..14a7de7a9ed --- /dev/null +++ b/config/src/test/resources/configs/function-test/defaultvalues.txt @@ -0,0 +1,47 @@ +bool_val false +int_val 5 +long_val 1234567890123 +double_val 41.23 +string_val "foo" +enum_val FOOBAR +refval :parent: +fileVal "vespa.log" +pathVal "pom.xml" +boolarr[1] +boolarr[0] false +intarr[0] +longarr[0] +doublearr[2] +doublearr[0] 2344 +doublearr[1] 123 +stringarr[1] +stringarr[0] "bar" +enumarr[1] +enumarr[0] VALUES +refarr[0] +fileArr[0] + +basicStruct.bar 3 +basicStruct.intArr[1] +basicStruct.intArr[0] 10 +rootStruct.inner0.index 11 +rootStruct.inner1.index 12 +rootStruct.innerArr[1] +rootStruct.innerArr[0].stringVal "deep" + +myarray[2] +myarray[0].stringval[2] +myarray[0].stringval[0] "baah" +myarray[0].stringval[1] "yikes" +myarray[0].refval ":parent:" +myarray[0].fileVal "command.com" +myarray[0].anotherarray[1] +myarray[0].anotherarray[0].foo 7 +myarray[0].myStruct.a 1 +myarray[1].stringval[0] +myarray[1].refval ":parent:" +myarray[1].fileVal "display.sys" +myarray[1].anotherarray[2] +myarray[1].anotherarray[0].foo 1 +myarray[1].anotherarray[1].foo 2 +myarray[1].myStruct.a -1 diff --git a/config/src/test/resources/configs/function-test/defaultvalues.xml b/config/src/test/resources/configs/function-test/defaultvalues.xml new file mode 100644 index 00000000000..80644c2fdb1 --- /dev/null +++ b/config/src/test/resources/configs/function-test/defaultvalues.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<config name="function-test"> + <bool_val>false</bool_val> + <int_val>5</int_val> + <long_val>1234567890123</long_val> + <double_val>41.23</double_val> + <string_val>foo</string_val> + <enum_val>FOOBAR</enum_val> + <refval>:parent:</refval> + <fileVal>vespa.log</fileVal> + + <boolarr index="0">false</boolarr> + <doublearr index="0">2344</doublearr> + <doublearr index="1">123</doublearr> + <stringarr index="0">bar</stringarr> + <enumarr index="0">VALUES</enumarr> + + <basicStruct> + <bar>3</bar> + <intArr index="0">10</intArr> + </basicStruct> + + <rootStruct> + <inner0> + <index>11</index> + </inner0> + <inner1> + <index>12</index> + </inner1> + <innerArr index="0"> + <stringVal>deep</stringVal> + </innerArr> + </rootStruct> + + <myarray index="0"> + <stringval index="0">baah</stringval> + <stringval index="1">yikes</stringval> + <refval>:parent:</refval> + <fileVal>command.com</fileVal> + <anotherarray index="0"> + <foo>7</foo> + </anotherarray> + <myStruct> + <a>1</a> + </myStruct> + </myarray> + <myarray index="1"> + <refval>:parent:</refval> + <fileVal>display.sys</fileVal> + <anotherarray index="0"> + <foo>1</foo> + </anotherarray> + <anotherarray index="1"> + <foo>2</foo> + </anotherarray> + <myStruct> + <a>-1</a> + </myStruct> + </myarray> + +</config> diff --git a/config/src/test/resources/configs/function-test/missingvalue.txt b/config/src/test/resources/configs/function-test/missingvalue.txt new file mode 100644 index 00000000000..9616ac3d27b --- /dev/null +++ b/config/src/test/resources/configs/function-test/missingvalue.txt @@ -0,0 +1,39 @@ +bool_val false +long_val 123 +double_val 41.23 +string_val "foo" +enum_val FOOBAR +refval ":parent:" +fileVal "msdos.sys" +boolarr[1] +boolarr[0] false +intarr[0] +longarr[0] +doublearr[2] +doublearr[0] 2344 +doublearr[1] 123 +stringarr[1] +stringarr[0] "bar" +enumarr[1] +enumarr[0] VALUES +refarr[0] +basicStruct.bar 3 +rootStruct.inner0.index 11 +rootStruct.inner1.index 12 +rootStruct.innerArr[0] +myarray[2] +myarray[0].stringval[2] +myarray[0].stringval[0] "baah" +myarray[0].stringval[1] "yikes" +myarray[0].refval ":parent:" +myarray[0].fileVal "command.com" +myarray[0].anotherarray[1] +myarray[0].anotherarray[0].foo 7 +myarray[0].myStruct.a 1 +myarray[1].stringval[0] +myarray[1].refval ":parent:" +myarray[1].fileVal "display.sys" +myarray[1].anotherarray[2] +myarray[1].anotherarray[0].foo 1 +myarray[1].anotherarray[1].foo 2 +myarray[1].myStruct.a 1 diff --git a/config/src/test/resources/configs/function-test/randomorder.txt b/config/src/test/resources/configs/function-test/randomorder.txt new file mode 100644 index 00000000000..bd91c24f3fb --- /dev/null +++ b/config/src/test/resources/configs/function-test/randomorder.txt @@ -0,0 +1,56 @@ +boolarr[1] +boolarr[0] false +int_with_def -14 +double_val 41.23 +double_with_def -12 +enumwithdef BAR2 +refval ":parent:" +refwithdef ":parent:" +intarr[0] +basicStruct.intArr[1] +basicStruct.intArr[0] 10 +doublearr[2] +doublearr[0] 2344 +doublearr[1] 123 +string_val "foo" +stringwithdef "bar and foo" +enum_val FOOBAR +stringarr[1] +stringarr[0] "bar" +basicStruct.bar 3 +long_with_def -333000333000 +enumarr[1] +enumarr[0] VALUES +refarr[0] +rootStruct.innerArr[1] +rootStruct.innerArr[0].stringVal "deep" +fileVal "autoexec.bat" +myarray[2] +myarray[0].intval -5 +myarray[0].myStruct.a 1 +myarray[0].enumval INNER +myarray[0].refval ":parent:" +myarray[0].anotherarray[1] +myarray[0].anotherarray[0].foo 7 +myarray[0].stringval[2] +myarray[0].stringval[0] "baah" +myarray[0].stringval[1] "yikes" +myarray[0].fileVal "file0" +myarray[1].stringval[0] +myarray[1].enumval INNER +myarray[1].anotherarray[2] +myarray[1].anotherarray[0].foo 1 +myarray[1].anotherarray[1].foo 2 +myarray[1].myStruct.a -1 +myarray[1].refval ":parent:" +myarray[1].intval 5 +myarray[1].fileVal "file1" +bool_val false +bool_with_def true +longarr[0] +rootStruct.inner1.index 12 +int_val 5 +rootStruct.inner0.index 11 +long_val 666000666000 +fileArr[0] +pathVal "pom.xml"
\ No newline at end of file diff --git a/config/src/test/resources/configs/function-test/variableaccess.txt b/config/src/test/resources/configs/function-test/variableaccess.txt new file mode 100644 index 00000000000..997de21750d --- /dev/null +++ b/config/src/test/resources/configs/function-test/variableaccess.txt @@ -0,0 +1,85 @@ +pathMap{"one"} "pom.xml" +bool_val false +bool_with_def true +int_val 5 +int_with_def -14 +long_val 12345678901 +long_with_def -9876543210 +double_val 41.23 +double_with_def -12 +string_val "foo" +stringwithdef "bar and foo" +enum_val FOOBAR +enumwithdef BAR2 +refval :parent: +refwithdef ":parent:" +fileVal "etc" +pathVal "pom.xml" +boolarr[1] +boolarr[0] false +intarr[0] +longarr[2] +longarr[0] 9223372036854775807 +longarr[1] -9223372036854775808 +doublearr[2] +doublearr[0] 2344 +doublearr[1] 123 +stringarr[1] +stringarr[0] "bar" +enumarr[1] +enumarr[0] VALUES +refarr[3] +refarr[0] ":parent:" +refarr[1] ":parent" +refarr[2] "parent:" +fileArr[1] +fileArr[0] "bin" +pathArr[1] +pathArr[0] "pom.xml" + +intMap{"one"} 1 +intMap{"two"} 2 +stringMap{"one"} "first" + +basicStruct.foo "basicFoo" +basicStruct.bar 3 +basicStruct.intArr[2] +basicStruct.intArr[0] 310 +basicStruct.intArr[1] 311 +rootStruct.inner0.index 11 +rootStruct.inner1.index 12 +rootStruct.innerArr[2] +rootStruct.innerArr[0].boolVal true +rootStruct.innerArr[0].stringVal "deep" +rootStruct.innerArr[1].boolVal false +rootStruct.innerArr[1].stringVal "blue a=\"escaped\"" + +myarray[2] +myarray[0].intval -5 +myarray[0].stringval[2] +myarray[0].stringval[0] "baah" +myarray[0].stringval[1] "yikes" +myarray[0].enumval INNER +myarray[0].refval :parent: +myarray[0].fileVal "file0" +myarray[0].anotherarray[1] +myarray[0].anotherarray[0].foo 7 +myarray[0].myStruct.a 1 +myarray[0].myStruct.b 2 +myarray[1].intval 5 +myarray[1].stringval[0] +myarray[1].enumval INNER +myarray[1].refval ":parent:" +myarray[1].fileVal "file1" +myarray[1].anotherarray[2] +myarray[1].anotherarray[0].foo 1 +myarray[1].anotherarray[1].foo 2 +myarray[1].myStruct.a -1 +myarray[1].myStruct.b -2 + +myStructMap{"one"}.myInt 1 +myStructMap{"one"}.myString "bull" +myStructMap{"one"}.myIntDef 2 +myStructMap{"one"}.myStringDef "bear" +myStructMap{"one"}.anotherMap{"anotherOne"}.anInt 3 +myStructMap{"one"}.anotherMap{"anotherOne"}.anIntDef 4 diff --git a/config/src/test/resources/configs/illegal/app.cfg b/config/src/test/resources/configs/illegal/app.cfg new file mode 100644 index 00000000000..d22d3d46c90 --- /dev/null +++ b/config/src/test/resources/configs/illegal/app.cfg @@ -0,0 +1,6 @@ +message "msg1" +times thisisnotanint +a[3] +a[0].name "a0" +a[1].name "a1" +a[2].name "a2" diff --git a/config/src/test/resources/configs/unicode/unicode.cfg b/config/src/test/resources/configs/unicode/unicode.cfg new file mode 100644 index 00000000000..d2c94abea86 --- /dev/null +++ b/config/src/test/resources/configs/unicode/unicode.cfg @@ -0,0 +1,2 @@ +unicodestring1 "Hei æøå 바둑 ÆØÅ hallo" +unicodestring2 "abc æøå 囲碁 ÆØÅ ABC" |