# HG changeset patch # User Andriy Petrus # Date 1375299051 14400 # Node ID 56e89524791ceec7d35bd004f0bfd2e8055fe0d2 # Parent 40e2c7a18bda8777186bb1835e1d38bcaf61a82f# Parent e7bdfcace2b039486953ee6ae3c74f7b6c5d3bf8 Merge diff -r 40e2c7a18bda -r 56e89524791c integration-tests/src/test/java/com/redhat/thermostat/itest/CliTest.java --- a/integration-tests/src/test/java/com/redhat/thermostat/itest/CliTest.java Wed Jul 31 15:10:25 2013 -0400 +++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/CliTest.java Wed Jul 31 15:30:51 2013 -0400 @@ -45,7 +45,6 @@ import org.junit.Test; import expectj.Spawn; -import expectj.TimeoutException; /** * Integration tests to exercise the basics of the thermostat command line. diff -r 40e2c7a18bda -r 56e89524791c integration-tests/src/test/java/com/redhat/thermostat/itest/IntegrationTest.java --- a/integration-tests/src/test/java/com/redhat/thermostat/itest/IntegrationTest.java Wed Jul 31 15:10:25 2013 -0400 +++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/IntegrationTest.java Wed Jul 31 15:30:51 2013 -0400 @@ -132,6 +132,39 @@ public static Spawn spawnThermostat(String... args) throws IOException { return spawnThermostat(false, args); } + + public static Spawn startStorage() throws Exception { + clearStorageDataDirectory(); + + Spawn storage = spawnThermostat("storage", "--start"); + try { + storage.expect("pid:"); + } catch (IOException e) { + // this may happen if storage is already running. + e.printStackTrace(); + String stdOutContents = storage.getCurrentStandardOutContents(); + + System.err.flush(); + System.out.flush(); + System.err.println("stdout was: -->" + stdOutContents +"<--"); + System.err.println("stderr was: -->" + storage.getCurrentStandardErrContents() + "<--"); + System.err.flush(); + assertFalse(stdOutContents.contains("Storage is already running with pid")); + throw new Exception("Something funny is going on when trying to start storage!", e); + } + storage.expectClose(); + + assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); + return storage; + } + + public static Spawn stopStorage() throws Exception { + Spawn storage = spawnThermostat("storage", "--stop"); + storage.expect("server shutdown complete"); + storage.expectClose(); + assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); + return storage; + } public static Spawn spawnThermostat(boolean localeDependent, String... args) throws IOException { ExpectJ expect = new ExpectJ(TIMEOUT_IN_SECONDS); @@ -213,6 +246,7 @@ } private static void killProcess(int processId) throws Exception { + System.err.println("Killing process with pid: " + processId); Runtime.getRuntime().exec("kill " + processId).waitFor(); } diff -r 40e2c7a18bda -r 56e89524791c integration-tests/src/test/java/com/redhat/thermostat/itest/MongoQueriesTest.java --- a/integration-tests/src/test/java/com/redhat/thermostat/itest/MongoQueriesTest.java Wed Jul 31 15:10:25 2013 -0400 +++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/MongoQueriesTest.java Wed Jul 31 15:30:51 2013 -0400 @@ -72,8 +72,6 @@ import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; import com.redhat.thermostat.vm.cpu.common.model.VmCpuStat; -import expectj.Spawn; - /* * This test class starts up a mongod instance and tests if thermostat * queries with expressions return what they supposed to return. @@ -117,13 +115,7 @@ @BeforeClass public static void setUpOnce() throws Exception { - clearStorageDataDirectory(); - - Spawn storage = spawnThermostat("storage", "--start"); - storage.expect("pid:"); - storage.expectClose(); - - assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); + startStorage(); addCpuData(4); } @@ -132,11 +124,7 @@ public static void tearDownOnce() throws Exception { deleteCpuData(); - Spawn storage = spawnThermostat("storage", "--stop"); - storage.expect("server shutdown complete"); - storage.expectClose(); - assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); - + stopStorage(); } /* diff -r 40e2c7a18bda -r 56e89524791c integration-tests/src/test/java/com/redhat/thermostat/itest/PluginTest.java --- a/integration-tests/src/test/java/com/redhat/thermostat/itest/PluginTest.java Wed Jul 31 15:10:25 2013 -0400 +++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/PluginTest.java Wed Jul 31 15:30:51 2013 -0400 @@ -74,7 +74,6 @@ shell.expectClose(); String stdOut = shell.getCurrentStandardOutContents(); - String stdErr = shell.getCurrentStandardErrContents(); assertTrue(stdOut.contains("list of commands")); assertTrue(stdOut.contains("help")); diff -r 40e2c7a18bda -r 56e89524791c integration-tests/src/test/java/com/redhat/thermostat/itest/StorageConnectionTest.java --- a/integration-tests/src/test/java/com/redhat/thermostat/itest/StorageConnectionTest.java Wed Jul 31 15:10:25 2013 -0400 +++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/StorageConnectionTest.java Wed Jul 31 15:30:51 2013 -0400 @@ -38,8 +38,6 @@ import java.io.IOException; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; @@ -49,22 +47,14 @@ public class StorageConnectionTest extends IntegrationTest { - @BeforeClass - public static void setUpOnce() throws IOException, TimeoutException, ExpectJException { - Spawn storage = spawnThermostat("storage", "--start"); - storage.expect("pid:"); - storage.expectClose(); - - assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); + // @BeforeClass // reinstate once we actually need storage running (see ignored tests) + public static void setUpOnce() throws Exception { + startStorage(); } - @AfterClass - public static void tearDownOnce() throws IOException, TimeoutException, ExpectJException { - Spawn storage = spawnThermostat("storage", "--stop"); - storage.expect("server shutdown complete"); - storage.expectClose(); - - assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); + // @AfterClass // reinstate once we actually need storage running + public static void tearDownOnce() throws Exception { + stopStorage(); } @Ignore //FIXME when keyring/preferences improvements have been made, un-Ignore diff -r 40e2c7a18bda -r 56e89524791c integration-tests/src/test/java/com/redhat/thermostat/itest/StorageTest.java --- a/integration-tests/src/test/java/com/redhat/thermostat/itest/StorageTest.java Wed Jul 31 15:10:25 2013 -0400 +++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/StorageTest.java Wed Jul 31 15:30:51 2013 -0400 @@ -46,11 +46,7 @@ public void startAndStopStorage() throws Exception { Spawn storage; - storage = spawnThermostat("storage", "--start"); - storage.expect("pid:"); - storage.expectClose(); - - assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); + storage = startStorage(); storage = spawnThermostat("storage", "--status"); storage.expect("Storage is running"); @@ -58,11 +54,7 @@ assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); - storage = spawnThermostat("storage", "--stop"); - storage.expect("server shutdown complete"); - storage.expectClose(); - - assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); + storage = stopStorage(); storage = spawnThermostat("storage", "--status"); storage.expect("Storage is not running"); diff -r 40e2c7a18bda -r 56e89524791c integration-tests/src/test/java/com/redhat/thermostat/itest/VmCommandsTest.java --- a/integration-tests/src/test/java/com/redhat/thermostat/itest/VmCommandsTest.java Wed Jul 31 15:10:25 2013 -0400 +++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/VmCommandsTest.java Wed Jul 31 15:30:51 2013 -0400 @@ -52,19 +52,15 @@ public class VmCommandsTest extends IntegrationTest { @BeforeClass - public static void setUpOnce() throws IOException, InterruptedException { - clearStorageDataDirectory(); - - Process startStorage = runThermostat("storage", "--start"); - startStorage.waitFor(); + public static void setUpOnce() throws Exception { + startStorage(); // TODO insert actual data into the database and test that } @AfterClass - public static void tearDownOnce() throws InterruptedException, IOException { - Process stopStorage = runThermostat("storage", "--stop"); - stopStorage.waitFor(); + public static void tearDownOnce() throws Exception { + stopStorage(); } @Ignore //FIXME when keyring/preferences improvements have been made, un-Ignore diff -r 40e2c7a18bda -r 56e89524791c integration-tests/src/test/java/com/redhat/thermostat/itest/WebAppTest.java --- a/integration-tests/src/test/java/com/redhat/thermostat/itest/WebAppTest.java Wed Jul 31 15:10:25 2013 -0400 +++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/WebAppTest.java Wed Jul 31 15:30:51 2013 -0400 @@ -93,8 +93,6 @@ import com.redhat.thermostat.web.client.internal.WebStorage; import com.redhat.thermostat.web.server.auth.Roles; -import expectj.Spawn; - /** * This test class starts up a mongod instance and a web storage instance * (in jetty container) in front of that. Tests should make their own @@ -230,13 +228,8 @@ @BeforeClass public static void setUpOnce() throws Exception { - clearStorageDataDirectory(); + startStorage(); - Spawn storage = spawnThermostat("storage", "--start"); - storage.expect("pid:"); - storage.expectClose(); - - assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); backupUsers = Files.createTempFile("itest-backup-thermostat-users", ""); backupRoles = Files.createTempFile("itest-backup-thermostat-roles", ""); backupRoles.toFile().deleteOnExit(); @@ -261,13 +254,10 @@ public static void tearDownOnce() throws Exception { deleteCpuData(); - Spawn storage = spawnThermostat("storage", "--stop"); - storage.expect("server shutdown complete"); - storage.expectClose(); - assertNoExceptions(storage.getCurrentStandardOutContents(), storage.getCurrentStandardErrContents()); - server.stop(); server.join(); + + stopStorage(); Files.copy(backupUsers, new File(THERMOSTAT_USERS_FILE).toPath(), StandardCopyOption.REPLACE_EXISTING); Files.copy(backupRoles, new File(THERMOSTAT_ROLES_FILE).toPath(), StandardCopyOption.REPLACE_EXISTING); diff -r 40e2c7a18bda -r 56e89524791c storage/core/src/main/java/com/redhat/thermostat/storage/core/HostRef.java --- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/HostRef.java Wed Jul 31 15:10:25 2013 -0400 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/HostRef.java Wed Jul 31 15:30:51 2013 -0400 @@ -36,11 +36,20 @@ package com.redhat.thermostat.storage.core; +/** + * Identifies an agent or a host + */ +// See http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1508 for +// more information public class HostRef implements Ref { private final String uid; private final String name; + /** + * @param id the UUID string that uniquely identifies an agent/host + * @param name the host name of an agent/host + */ public HostRef(String id, String name) { this.uid = id; this.name = name; diff -r 40e2c7a18bda -r 56e89524791c storage/core/src/main/java/com/redhat/thermostat/storage/dao/AgentInfoDAO.java --- a/storage/core/src/main/java/com/redhat/thermostat/storage/dao/AgentInfoDAO.java Wed Jul 31 15:10:25 2013 -0400 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/dao/AgentInfoDAO.java Wed Jul 31 15:30:51 2013 -0400 @@ -45,6 +45,9 @@ import com.redhat.thermostat.storage.core.Key; import com.redhat.thermostat.storage.model.AgentInformation; +/** + * Access information about agents that agents publish to storage. + */ @Service public interface AgentInfoDAO extends Countable { @@ -60,17 +63,47 @@ ALIVE_KEY, CONFIG_LISTEN_ADDRESS); + /** + * Get information about all known agents. + * + * @return a {@link List} of {@link AgentInformation} for all agents + * who have published their information. Will be empty if there is no + * information. + */ List getAllAgentInformation(); + /** + * Get information about all alive agents. + * + * @return a {@link List} of {@link AgentInformation} for all alive + * agents who have published their information. Will be empty if there + * is no information or no alive agents. + */ List getAliveAgents(); + /** + * Get information about a specific agent. + * + * @return a {@link AgentInformation} describing information about the agent + * indicated by {@code agentRef}. {@code null} if no information about the + * agent could be located. + */ AgentInformation getAgentInformation(HostRef agentRef); + /** + * Publish information about agent into the storage. + */ void addAgentInformation(AgentInformation agentInfo); + /** + * Update information about an existing agent. No changes will be performed + * if there is no matching agent. + */ void updateAgentInformation(AgentInformation agentInfo); + /** + * Remove information about an agent that was published to storage. + */ void removeAgentInformation(AgentInformation agentInfo); } - diff -r 40e2c7a18bda -r 56e89524791c storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/StatementDescriptorParser.java --- a/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/StatementDescriptorParser.java Wed Jul 31 15:10:25 2013 -0400 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/StatementDescriptorParser.java Wed Jul 31 15:30:51 2013 -0400 @@ -428,17 +428,26 @@ String stringTerm = getStringTerm(term); return stringTerm; } catch (DescriptorParsingException e) { - // must be int or boolean + // Must be integer (long/int) or boolean. First check for boolean, + // then for long and regular ints. + if (term.equals(Boolean.toString(false)) || term.equals(Boolean.toString(true))) { + boolean boolVal = Boolean.parseBoolean(term); + return boolVal; + } + // Next, parse long or int. try { + int lastCharInTokenIndex = term.length() - 1; + // preceding l/L indicate long integer types. + if (term.charAt(lastCharInTokenIndex) == 'L' || term.charAt(lastCharInTokenIndex) == 'l') { + long longVal = Long.parseLong(term.substring(0, lastCharInTokenIndex)); + return longVal; + } + // must be integer or some invalid type int intVal = Integer.parseInt(term); return intVal; } catch (NumberFormatException nfe) { - if (term.equals(Boolean.toString(false)) || term.equals(Boolean.toString(true))) { - boolean boolVal = Boolean.parseBoolean(term); - return boolVal; - } else { - throw new DescriptorParsingException("Illegal terminal type. Token was ->" + term + "<-"); - } + String msg = "Illegal terminal type. Token was ->" + term + "<-"; + throw new DescriptorParsingException(msg); } } } diff -r 40e2c7a18bda -r 56e89524791c storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/StatementDescriptorParserTest.java --- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/StatementDescriptorParserTest.java Wed Jul 31 15:10:25 2013 -0400 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/StatementDescriptorParserTest.java Wed Jul 31 15:30:51 2013 -0400 @@ -110,6 +110,134 @@ } @Test + public void testParseLongInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 30000000003L"; + doTestType(descrString, 30000000003L, 0); + } + + @Test + public void testParseLongInWhere2() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 30000000003l"; + doTestType(descrString, 30000000003L, 0); + } + + @Test + public void testParseLongInWhere3() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 3l"; + doTestType(descrString, 3L, 0); + } + + @Test + public void testParseLongInWhere4() throws DescriptorParsingException { + long val = Long.MIN_VALUE; + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != " + val + "l"; + doTestType(descrString, val, 0); + } + + @Test + public void testParseIntInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 30000"; + doTestType(descrString, 30000, 0); + } + + @Test + public void testParseIntInWhere2() throws DescriptorParsingException { + int val = Integer.MAX_VALUE - 1; + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != " + val; + doTestType(descrString, val, 0); + } + + @Test + public void testParseIntInWhere3() throws DescriptorParsingException { + int val = Integer.MIN_VALUE; + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != " + val; + doTestType(descrString, val, 0); + } + + @Test + public void testParseBooleanInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != true"; + doTestType(descrString, true, 0); + } + + @Test + public void testParseBooleanInWhere2() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != false"; + doTestType(descrString, false, 0); + } + + @Test + public void testParseStringInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 'testing'"; + doTestType(descrString, "testing", 0); + } + + @Test + public void testParseStringTypeFreeVarInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?s"; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(String.class); + unfinished.setParameterIndex(0); + doTestType(descrString, unfinished, 1); + } + + @Test + public void testParseIntTypeFreeVarInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?i"; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(Integer.class); + unfinished.setParameterIndex(0); + doTestType(descrString, unfinished, 1); + } + + @Test + public void testParseLongTypeFreeVarInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?l"; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(Long.class); + unfinished.setParameterIndex(0); + doTestType(descrString, unfinished, 1); + } + + @Test + public void testParseBooleanTypeFreeVarInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?b"; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(Boolean.class); + unfinished.setParameterIndex(0); + doTestType(descrString, unfinished, 1); + } + + private void doTestType(String strDesc, Object bVal, int expNumFreeVars) throws DescriptorParsingException { + StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, strDesc); + parser = new StatementDescriptorParser<>(storage, desc); + ParsedStatementImpl statement = (ParsedStatementImpl)parser.parse(); + assertEquals(expNumFreeVars, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression tree = statement.getSuffixExpression(); + assertNull(tree.getLimitExpn()); + assertNull(tree.getSortExpn()); + assertNotNull(tree.getWhereExpn()); + + WhereExpression expected = new WhereExpression(); + BinaryExpressionNode notEquals = new BinaryExpressionNode(expected.getRoot()); + expected.getRoot().setValue(notEquals); + notEquals.setOperator(BinaryComparisonOperator.NOT_EQUAL_TO); + TerminalNode a = new TerminalNode(notEquals); + a.setValue(new Key("a", false)); + TerminalNode b = new TerminalNode(notEquals); + b.setValue(bVal); + notEquals.setLeftChild(a); + notEquals.setRightChild(b); + + assertTrue(WhereExpressions.equals(expected, tree.getWhereExpn())); + } + + @Test public void testParseNotEqualComparisonInWhere() throws DescriptorParsingException { String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 'b'"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); @@ -1109,12 +1237,29 @@ } @Test + public void rejectLongValAsIntType() throws DescriptorParsingException { + // 30000000003 > Integer.MAX_VALUE; needs to be preceded by 'l/L' + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 30000000003"; + StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new StatementDescriptorParser<>(storage, desc); + + try { + parser.parse(); + fail("should not parse"); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("Illegal terminal type.")); + } + } + + @Test public void rejectLimitWhichIsNotInt() { String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT illegal"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); parser = new StatementDescriptorParser<>(storage, desc); try { parser.parse(); + fail("should not parse"); } catch (DescriptorParsingException e) { assertEquals("Invalid limit expression. 'illegal' not an integer", e.getMessage()); } @@ -1127,6 +1272,7 @@ parser = new StatementDescriptorParser<>(storage, desc); try { parser.parse(); + fail("should not parse"); } catch (DescriptorParsingException e) { // pass assertTrue(e.getMessage().contains("Expected string value. Got term ->a<-")); @@ -1141,6 +1287,7 @@ parser = new StatementDescriptorParser<>(storage, desc); try { parser.parse(); + fail("should not parse"); } catch (DescriptorParsingException e) { // pass assertEquals("Unknown type of free parameter: '?'", e.getMessage()); @@ -1154,6 +1301,7 @@ parser = new StatementDescriptorParser<>(storage, desc); try { parser.parse(); + fail("should not parse"); } catch (DescriptorParsingException e) { // pass assertTrue(e.getMessage().contains("Expected ASC or DSC")); @@ -1167,6 +1315,7 @@ parser = new StatementDescriptorParser<>(storage, desc); try { parser.parse(); + fail("should not parse"); } catch (DescriptorParsingException e) { // pass } @@ -1181,6 +1330,7 @@ parser = new StatementDescriptorParser<>(storage, desc); try { parser.parse(); + fail("should not parse"); } catch (DescriptorParsingException e) { // pass } @@ -1194,6 +1344,7 @@ parser = new StatementDescriptorParser<>(storage, desc); try { parser.parse(); + fail("should not parse"); } catch (DescriptorParsingException e) { // pass } @@ -1207,6 +1358,7 @@ parser = new StatementDescriptorParser<>(storage, desc); try { parser.parse(); + fail("should not parse"); } catch (DescriptorParsingException e) { // pass } @@ -1220,6 +1372,7 @@ parser = new StatementDescriptorParser<>(storage, desc); try { parser.parse(); + fail("should not parse"); } catch (DescriptorParsingException e) { // pass }