ASTERIXDB-1711: Move test infra from test.aql Change-Id: I5d631a2028fb80c823fbaafb7269f15d07685dc9 Reviewed-on: https://asterix-gerrit.ics.uci.edu/1512 Tested-by: Jenkins <jenk...@fulliautomatix.ics.uci.edu> Integration-Tests: Jenkins <jenk...@fulliautomatix.ics.uci.edu> Reviewed-by: abdullah alamoudi <bamou...@gmail.com>
Project: http://git-wip-us.apache.org/repos/asf/asterixdb/repo Commit: http://git-wip-us.apache.org/repos/asf/asterixdb/commit/452ec9f6 Tree: http://git-wip-us.apache.org/repos/asf/asterixdb/tree/452ec9f6 Diff: http://git-wip-us.apache.org/repos/asf/asterixdb/diff/452ec9f6 Branch: refs/heads/master Commit: 452ec9f6fa25e266713d9eaebb5909edef5c1149 Parents: ea666c9 Author: Till Westmann <ti...@apache.org> Authored: Thu Feb 23 09:12:50 2017 -0800 Committer: Till Westmann <ti...@apache.org> Committed: Thu Feb 23 16:48:32 2017 -0800 ---------------------------------------------------------------------- asterixdb/asterix-app/pom.xml | 4 + .../asterix/app/external/TestLibrarian.java | 2 +- .../test/common/ComparisonException.java | 27 + .../asterix/test/common/ITestLibrarian.java | 31 + .../asterix/test/common/ResultExtractor.java | 126 ++ .../test/common/SingleLinePrettyPrinter.java | 405 ++++++ .../asterix/test/common/TestExecutor.java | 1258 +++++++++++++++++ .../org/apache/asterix/test/dml/DmlTest.java | 2 +- .../asterix/test/metadata/MetadataTest.java | 2 +- .../asterix/test/runtime/LangExecutionUtil.java | 2 +- .../asterix/test/sqlpp/ParserTestExecutor.java | 4 +- .../asterix/test/sqlpp/RuntimeParserTest.java | 2 +- asterixdb/asterix-common/pom.xml | 22 +- .../apache/asterix/test/aql/ITestLibrarian.java | 31 - .../asterix/test/aql/ResultExtractor.java | 127 -- .../test/aql/SingleLinePrettyPrinter.java | 404 ------ .../apache/asterix/test/aql/TestExecutor.java | 1259 ------------------ .../asterix/test/base/ComparisonException.java | 27 - .../installer/test/AbstractExecutionIT.java | 2 +- .../test/AsterixClusterLifeCycleIT.java | 2 +- .../test/AsterixExternalLibraryIT.java | 2 +- .../installer/test/AsterixLifecycleIT.java | 2 +- .../installer/test/AsterixRestartIT.java | 2 +- .../installer/test/ClusterExecutionIT.java | 2 +- .../installer/test/MetadataReplicationIT.java | 2 +- .../asterix/installer/test/ReplicationIT.java | 2 +- .../installer/transaction/DmlRecoveryIT.java | 2 +- .../installer/transaction/RecoveryIT.java | 2 +- .../server/test/NCServiceExecutionIT.java | 2 +- .../server/test/SampleLocalClusterIT.java | 2 +- asterixdb/asterix-yarn/pom.xml | 2 +- .../aoya/test/AsterixYARNLibraryTestIT.java | 2 +- 32 files changed, 1873 insertions(+), 1890 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/asterixdb/blob/452ec9f6/asterixdb/asterix-app/pom.xml ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/pom.xml b/asterixdb/asterix-app/pom.xml index 7ce71b7..74102a1 100644 --- a/asterixdb/asterix-app/pom.xml +++ b/asterixdb/asterix-app/pom.xml @@ -357,6 +357,10 @@ <artifactId>httpcore</artifactId> </dependency> <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient</artifactId> + </dependency> + <dependency> <groupId>org.apache.asterix</groupId> <artifactId>asterix-lang-common</artifactId> <version>${project.version}</version> http://git-wip-us.apache.org/repos/asf/asterixdb/blob/452ec9f6/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/external/TestLibrarian.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/external/TestLibrarian.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/external/TestLibrarian.java index ba34024..e92b5e8 100644 --- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/external/TestLibrarian.java +++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/external/TestLibrarian.java @@ -28,7 +28,7 @@ import org.apache.asterix.common.exceptions.ACIDException; import org.apache.asterix.common.exceptions.AsterixException; import org.apache.asterix.common.library.ILibraryManager; import org.apache.asterix.event.service.AsterixEventServiceUtil; -import org.apache.asterix.test.aql.ITestLibrarian; +import org.apache.asterix.test.common.ITestLibrarian; import org.apache.commons.io.FileUtils; import org.apache.hyracks.algebricks.common.utils.Pair; import org.apache.hyracks.api.exceptions.HyracksDataException; http://git-wip-us.apache.org/repos/asf/asterixdb/blob/452ec9f6/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ComparisonException.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ComparisonException.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ComparisonException.java new file mode 100644 index 0000000..da5c8f6 --- /dev/null +++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ComparisonException.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.asterix.test.common; + +public class ComparisonException extends Exception { + private static final long serialVersionUID = 1L; + + public ComparisonException(String message) { + super(message); + } +} http://git-wip-us.apache.org/repos/asf/asterixdb/blob/452ec9f6/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ITestLibrarian.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ITestLibrarian.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ITestLibrarian.java new file mode 100644 index 0000000..d661710 --- /dev/null +++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ITestLibrarian.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.asterix.test.common; + +import java.io.IOException; +import java.rmi.RemoteException; + +import org.apache.asterix.common.exceptions.ACIDException; +import org.apache.asterix.common.exceptions.AsterixException; + +public interface ITestLibrarian { + public void install(String dvName, String libName, String libPath) throws IOException, Exception; + + public void uninstall(String dvName, String libName) throws RemoteException, AsterixException, ACIDException; +} http://git-wip-us.apache.org/repos/asf/asterixdb/blob/452ec9f6/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java new file mode 100644 index 0000000..3531211 --- /dev/null +++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/ResultExtractor.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.asterix.test.common; + +import java.io.InputStream; +import java.io.StringWriter; +import java.nio.charset.Charset; +import java.util.Iterator; +import java.util.logging.Logger; + +import org.apache.asterix.common.exceptions.AsterixException; +import org.apache.commons.io.IOUtils; + +import com.fasterxml.jackson.core.PrettyPrinter; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Iterators; + +/** + * extracts results from the response of the QueryServiceServlet. + * As the response is not necessarily valid JSON, non-JSON content has to be extracted in some cases. + * The current implementation creates a toomany copies of the data to be usable for larger results. + */ +public class ResultExtractor { + + private static final Logger LOGGER = Logger.getLogger(ResultExtractor.class.getName()); + + public static InputStream extract(InputStream resultStream) throws Exception { + ObjectMapper om = new ObjectMapper(); + String resultStr = IOUtils.toString(resultStream, Charset.defaultCharset()); + PrettyPrinter singleLine = new SingleLinePrettyPrinter(); + ObjectNode result = om.readValue(resultStr, ObjectNode.class); + + LOGGER.fine("+++++++\n" + result + "\n+++++++\n"); + + String type = ""; + String status = ""; + String results = ""; + String field = ""; + for (Iterator<String> sIter = result.fieldNames(); sIter.hasNext();) { + field = sIter.next(); + switch (field) { + case "requestID": + break; + case "signature": + break; + case "status": + status = om.writeValueAsString(result.get(field)); + break; + case "type": + type = om.writeValueAsString(result.get(field)); + break; + case "metrics": + LOGGER.fine(om.writeValueAsString(result.get(field))); + break; + case "errors": + JsonNode errors = result.get(field).get(0).get("msg"); + throw new AsterixException(errors.asText()); + case "results": + if (result.get(field).size() <= 1) { + if (result.get(field).size() == 0) { + results = ""; + } else if (result.get(field).isArray()) { + if (result.get(field).get(0).isTextual()) { + results = result.get(field).get(0).asText(); + } else { + ObjectMapper omm = new ObjectMapper(); + omm.setDefaultPrettyPrinter(singleLine); + omm.enable(SerializationFeature.INDENT_OUTPUT); + results = omm.writer(singleLine).writeValueAsString(result.get(field)); + } + } else { + results = om.writeValueAsString(result.get(field)); + } + } else { + StringBuilder sb = new StringBuilder(); + JsonNode[] fields = Iterators.toArray(result.get(field).elements(), JsonNode.class); + if (fields.length > 1) { + for (JsonNode f : fields) { + if (f.isObject()) { + sb.append(om.writeValueAsString(f)); + } else { + sb.append(f.asText()); + } + } + } + results = sb.toString(); + } + break; + default: + throw new AsterixException(field + "unanticipated field"); + } + } + + return IOUtils.toInputStream(results); + } + + public static String extractHandle(InputStream resultStream) throws Exception { + final Charset utf8 = Charset.forName("UTF-8"); + ObjectMapper om = new ObjectMapper(); + String result = IOUtils.toString(resultStream, utf8); + ObjectNode resultJson = om.readValue(result, ObjectNode.class); + JsonNode handle = resultJson.get("handle"); + ObjectNode res = om.createObjectNode(); + res.set("handle", handle); + return om.writeValueAsString(res); + } +} http://git-wip-us.apache.org/repos/asf/asterixdb/blob/452ec9f6/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/SingleLinePrettyPrinter.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/SingleLinePrettyPrinter.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/SingleLinePrettyPrinter.java new file mode 100644 index 0000000..2932ca4 --- /dev/null +++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/SingleLinePrettyPrinter.java @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.asterix.test.common; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.PrettyPrinter; +import com.fasterxml.jackson.core.SerializableString; +import com.fasterxml.jackson.core.io.SerializedString; +import com.fasterxml.jackson.core.util.Instantiatable; + +/** + * Default {@link PrettyPrinter} implementation that uses 2-space + * indentation with platform-default linefeeds. + * Usually this class is not instantiated directly, but instead + * method {@link JsonGenerator#useSingleLinePrettyPrinter} is + * used, which will use an instance of this class for operation. + */ +@SuppressWarnings("serial") +public class SingleLinePrettyPrinter + implements PrettyPrinter, Instantiatable<SingleLinePrettyPrinter>, java.io.Serializable { + private static final long serialVersionUID = 1; + + /** + * Constant that specifies default "root-level" separator to use between + * root values: a single space character. + * + * @since 2.1 + */ + public final static SerializedString DEFAULT_ROOT_VALUE_SEPARATOR = new SerializedString(" "); + + /** + * Interface that defines objects that can produce indentation used + * to separate object entries and array values. Indentation in this + * context just means insertion of white space, independent of whether + * linefeeds are output. + */ + public interface Indenter { + void writeIndentation(JsonGenerator jg, int level) throws IOException; + + /** + * @return True if indenter is considered inline (does not add linefeeds), + * false otherwise + */ + boolean isInline(); + } + + // // // Config, indentation + + /** + * By default, let's use only spaces to separate array values. + */ + protected Indenter _arrayIndenter = FixedSpaceIndenter.instance; + + /** + * By default, let's use linefeed-adding indenter for separate + * object entries. We'll further configure indenter to use + * system-specific linefeeds, and 2 spaces per level (as opposed to, + * say, single tabs) + */ + protected Indenter _objectIndenter = new FixedSpaceIndenter(); + + /** + * String printed between root-level values, if any. + */ + protected final SerializableString _rootSeparator; + + // // // Config, other white space configuration + + /** + * By default we will add spaces around colons used to + * separate object fields and values. + * If disabled, will not use spaces around colon. + */ + protected boolean _spacesInObjectEntries = true; + + // // // State: + + /** + * Number of open levels of nesting. Used to determine amount of + * indentation to use. + */ + protected transient int _nesting; + + /* + /********************************************************** + /* Life-cycle (construct, configure) + /********************************************************** + */ + + public SingleLinePrettyPrinter() { + this(DEFAULT_ROOT_VALUE_SEPARATOR); + } + + /** + * Constructor that specifies separator String to use between root values; + * if null, no separator is printed. + * <p> + * Note: simply constructs a {@link SerializedString} out of parameter, + * calls {@link #SingleLinePrettyPrinter(SerializableString)} + * + * @param rootSeparator + * @since 2.1 + */ + public SingleLinePrettyPrinter(String rootSeparator) { + this((rootSeparator == null) ? null : new SerializedString(rootSeparator)); + } + + /** + * Constructor that specifies separator String to use between root values; + * if null, no separator is printed. + * + * @param rootSeparator + * @since 2.1 + */ + public SingleLinePrettyPrinter(SerializableString rootSeparator) { + _rootSeparator = rootSeparator; + } + + public SingleLinePrettyPrinter(SingleLinePrettyPrinter base) { + this(base, base._rootSeparator); + } + + public SingleLinePrettyPrinter(SingleLinePrettyPrinter base, SerializableString rootSeparator) { + _arrayIndenter = base._arrayIndenter; + _objectIndenter = base._objectIndenter; + _spacesInObjectEntries = base._spacesInObjectEntries; + _nesting = base._nesting; + + _rootSeparator = rootSeparator; + } + + public SingleLinePrettyPrinter withRootSeparator(SerializableString rootSeparator) { + if (_rootSeparator == rootSeparator || (rootSeparator != null && rootSeparator.equals(_rootSeparator))) { + return this; + } + return new SingleLinePrettyPrinter(this, rootSeparator); + } + + /** + * @since 2.6.0 + */ + public SingleLinePrettyPrinter withRootSeparator(String rootSeparator) { + return withRootSeparator((rootSeparator == null) ? null : new SerializedString(rootSeparator)); + } + + public void indentArraysWith(Indenter i) { + _arrayIndenter = (i == null) ? NopIndenter.instance : i; + } + + public void indentObjectsWith(Indenter i) { + _objectIndenter = (i == null) ? NopIndenter.instance : i; + } + + /** + * @deprecated Since 2.3 use {@link #withSpacesInObjectEntries} and {@link #withoutSpacesInObjectEntries()} + */ + @Deprecated + public void spacesInObjectEntries(boolean b) { + _spacesInObjectEntries = b; + } + + /** + * @since 2.3 + */ + public SingleLinePrettyPrinter withArrayIndenter(Indenter i) { + if (i == null) { + i = NopIndenter.instance; + } + if (_arrayIndenter == i) { + return this; + } + SingleLinePrettyPrinter pp = new SingleLinePrettyPrinter(this); + pp._arrayIndenter = i; + return pp; + } + + /** + * @since 2.3 + */ + public SingleLinePrettyPrinter withObjectIndenter(Indenter i) { + if (i == null) { + i = NopIndenter.instance; + } + if (_objectIndenter == i) { + return this; + } + SingleLinePrettyPrinter pp = new SingleLinePrettyPrinter(this); + pp._objectIndenter = i; + return pp; + } + + /** + * "Mutant factory" method that will return a pretty printer instance + * that does use spaces inside object entries; if 'this' instance already + * does this, it is returned; if not, a new instance will be constructed + * and returned. + * + * @since 2.3 + */ + public SingleLinePrettyPrinter withSpacesInObjectEntries() { + return _withSpaces(true); + } + + /** + * "Mutant factory" method that will return a pretty printer instance + * that does not use spaces inside object entries; if 'this' instance already + * does this, it is returned; if not, a new instance will be constructed + * and returned. + * + * @since 2.3 + */ + public SingleLinePrettyPrinter withoutSpacesInObjectEntries() { + return _withSpaces(false); + } + + protected SingleLinePrettyPrinter _withSpaces(boolean state) { + if (_spacesInObjectEntries == state) { + return this; + } + SingleLinePrettyPrinter pp = new SingleLinePrettyPrinter(this); + pp._spacesInObjectEntries = state; + return pp; + } + + /* + /********************************************************** + /* Instantiatable impl + /********************************************************** + */ + + @Override + public SingleLinePrettyPrinter createInstance() { + return new SingleLinePrettyPrinter(this); + } + + /* + /********************************************************** + /* PrettyPrinter impl + /********************************************************** + */ + + @Override + public void writeRootValueSeparator(JsonGenerator jg) throws IOException { + if (_rootSeparator != null) { + jg.writeRaw(_rootSeparator); + } + } + + @Override + public void writeStartObject(JsonGenerator jg) throws IOException { + jg.writeRaw('{'); + ++_nesting; + } + + @Override + public void beforeObjectEntries(JsonGenerator jg) throws IOException { + _objectIndenter.writeIndentation(jg, _nesting); + } + + /** + * Method called after an object field has been output, but + * before the value is output. + * <p> + * Default handling (without pretty-printing) will output a single + * colon to separate the two. Pretty-printer is + * to output a colon as well, but can surround that with other + * (white-space) decoration. + */ + @Override + public void writeObjectFieldValueSeparator(JsonGenerator jg) throws IOException { + if (_spacesInObjectEntries) { + jg.writeRaw(": "); + } else { + jg.writeRaw(':'); + } + } + + /** + * Method called after an object entry (field:value) has been completely + * output, and before another value is to be output. + * <p> + * Default handling (without pretty-printing) will output a single + * comma to separate the two. Pretty-printer is + * to output a comma as well, but can surround that with other + * (white-space) decoration. + */ + @Override + public void writeObjectEntrySeparator(JsonGenerator jg) throws IOException { + jg.writeRaw(','); + _objectIndenter.writeIndentation(jg, _nesting); + } + + @Override + public void writeEndObject(JsonGenerator jg, int nrOfEntries) throws IOException { + --_nesting; + if (nrOfEntries > 1) { + _objectIndenter.writeIndentation(jg, _nesting); + } else { + jg.writeRaw(' '); + } + jg.writeRaw('}'); + } + + @Override + public void writeStartArray(JsonGenerator jg) throws IOException { + ++_nesting; + jg.writeRaw('['); + } + + @Override + public void beforeArrayValues(JsonGenerator jg) throws IOException { + _arrayIndenter.writeIndentation(jg, _nesting); + } + + /** + * Method called after an array value has been completely + * output, and before another value is to be output. + * <p> + * Default handling (without pretty-printing) will output a single + * comma to separate the two. Pretty-printer is + * to output a comma as well, but can surround that with other + * (white-space) decoration. + */ + @Override + public void writeArrayValueSeparator(JsonGenerator gen) throws IOException { + gen.writeRaw(','); + _arrayIndenter.writeIndentation(gen, _nesting); + } + + @Override + public void writeEndArray(JsonGenerator gen, int nrOfValues) throws IOException { + --_nesting; + + if (_nesting == 0) { + gen.writeRaw('\n'); + } + if (nrOfValues > 1) { + _arrayIndenter.writeIndentation(gen, _nesting); + } else { + gen.writeRaw(' '); + } + gen.writeRaw(']'); + } + + /* + /********************************************************** + /* Helper classes + /********************************************************** + */ + + /** + * Dummy implementation that adds no indentation whatsoever + */ + public static class NopIndenter implements Indenter, java.io.Serializable { + public static final NopIndenter instance = new NopIndenter(); + + @Override + public void writeIndentation(JsonGenerator jg, int level) throws IOException { + } + + @Override + public boolean isInline() { + return true; + } + } + + /** + * This is a very simple indenter that only adds a + * single space for indentation. It is used as the default + * indenter for array values. + */ + public static class FixedSpaceIndenter extends NopIndenter { + @SuppressWarnings("hiding") + public static final FixedSpaceIndenter instance = new FixedSpaceIndenter(); + + @Override + public void writeIndentation(JsonGenerator jg, int level) throws IOException { + jg.writeRaw(' '); + } + + @Override + public boolean isInline() { + return true; + } + } +}