Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-22975

Exception parsing "implies" expression - Label.parseExpression("a->b")

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: Major Major
    • core
    • None
    • 2.313

      While writing integration tests for a Jenkins plugin I identified the following Bugs and inconsistencies regarding Label parsing and handling.
      I wrote additional tests reproducing the findings.

      1. Parsing "a->b" fails with an Exception. With blanks "a -> b" it works, but Label.getName() again returns "a->b" without blanks - which cannot be parsed!

        /**
         * Implies parsing fails with Exception. "a->b" cannot be parsed (see labelB).
         * On the other hand Implies.getName() returns exactly this String for a valid
         * instance. See labelA parsed from "a -> b" with blanks (which works).
         */
        @Test
        public void testParseExpressionImplies() throws IOException, ANTLRException {
          //This one works
          Label labelA = Label.parseExpression("a -> b");
          assertEquals(Implies.class, labelA.getClass());
          assertEquals("a->b", labelA.getName());
          
          //TokenStreamRecognitionException: line 1:3: unexpected char: '>'
          //at hudson.model.labels.LabelExpressionLexer.nextToken(LabelExpressionLexer.java:127)
          Label labelB = Label.parseExpression("a->b");
          
          assertEquals(Implies.class, labelB.getClass());
          assertEquals("a->b", labelB.getName());
        }
      

      2. When assigning an "Implies" instance to a project the result is a "LabelAtom" which is not correct and causes issues!

        /**
         * Setting the Implies expression without blanks does not work at all
         * (see {@link #testParseExpressionImplies}).
         * Setting it with blanks works, but after saving the job the result
         * is a LabelAtom instead of a LabelExpression!
         */
        @Test
        public void testImpliesViaAPI() throws IOException, ANTLRException {
          Label label = Label.parseExpression("a -> b");
          assertEquals(Implies.class, label.getClass());
          assertEquals("a->b", label.getName());
          FreeStyleProject project = j.createFreeStyleProject();
          project.setAssignedLabel(label);
          project.save();
      
          assertEquals("a->b", project.getAssignedLabel().getName());
          assertEquals("\"a->b\"", project.getAssignedLabelString());
      
          //AssertionError: expected:<class hudson.model.labels.LabelExpression$Implies> 
          //                 but was:<class hudson.model.labels.LabelAtom>
          assertEquals(Implies.class, project.getAssignedLabel().getClass());
        }
      

      3. The same problem occurs in the UI when using "a->b" without blanks

        /**
         * Setting the Implies expression in the UI (correctly) without blanks works, but the result
         * is a LabelAtom instead of a LabelExpression!
         */
        @Test
        public void testImpliesViaUI() throws Exception {
          FreeStyleProject project = j.createFreeStyleProject();
          setViaReflection(project, AbstractProject.class, "assignedNode", "a->b");
          project.save();
          
          assertEquals("a->b", project.getAssignedLabel().getName());
          assertEquals("\"a->b\"", project.getAssignedLabelString());
      
          //AssertionError: expected:<class hudson.model.labels.LabelExpression$Implies> 
          //                 but was:<class hudson.model.labels.LabelAtom>
          assertEquals(Implies.class, project.getAssignedLabel().getClass());
        }
      

      3b. With blanks it is OK:

        /**
         * Saving the Implies expression with blanks in the UI works properly.
         */
        @Test
        public void testImpliesViaUIWithBlanks() throws Exception {
          FreeStyleProject project = j.createFreeStyleProject();
          setViaReflection(project, AbstractProject.class, "assignedNode", "a -> b");
          project.save();
          
          assertEquals(Implies.class, project.getAssignedLabel().getClass());
          assertEquals("a->b", project.getAssignedLabel().getName());
          assertEquals("a -> b", project.getAssignedLabelString());
        }
      

      4. I also identified some inconsistency regarding "master" when using UI and API:

        /**
         * Setting "master" as label in UI works properly as expected.
         */
        @Test
        public void testMasterViaUI() throws Exception {
          FreeStyleProject project = j.createFreeStyleProject();
          setViaReflection(project, AbstractProject.class, "assignedNode", "master");
          project.save();
          assertEquals("master", project.getAssignedLabel().getName());
          assertEquals("master", project.getAssignedLabelString());
        }
      
        /**
         * Setting "master" as label via API results in a different, unexpected behavior.
         * project.getAssignedLabelString returns null. 
         */
        @Test
        public void testMasterViaAPI() throws IOException {
          Label label = Label.get("master");
          assertEquals("master", label.getName());
          FreeStyleProject project = j.createFreeStyleProject();
          project.save();
          project.setAssignedLabel(label);
          assertEquals("master", project.getAssignedLabel().getName());
          
          // value is null:
          assertEquals("master", project.getAssignedLabelString());
        }
      

      Here the whole test class:

      import static org.junit.Assert.assertEquals;
      import hudson.model.AbstractProject;
      import hudson.model.FreeStyleProject;
      import hudson.model.Label;
      import hudson.model.labels.LabelExpression.Iff;
      import hudson.model.labels.LabelExpression.Implies;
      
      import java.io.IOException;
      import java.lang.reflect.Field;
      
      import org.junit.Rule;
      import org.junit.Test;
      import org.jvnet.hudson.test.JenkinsRule;
      
      import antlr.ANTLRException;
      
      public class ReproduceJenkinsLabelBugs
      {
      
        @Rule
        public JenkinsRule j = new JenkinsRule();
      
        /**
         * Setting "master" as label in UI works properly as expected.
         */
        @Test
        public void testMasterViaUI() throws Exception {
          FreeStyleProject project = j.createFreeStyleProject();
          setViaReflection(project, AbstractProject.class, "assignedNode", "master");
          project.save();
          assertEquals("master", project.getAssignedLabel().getName());
          assertEquals("master", project.getAssignedLabelString());
        }
      
        /**
         * Setting "master" as label via API results in a different, unexpected behavior.
         * project.getAssignedLabelString returns null. 
         */
        @Test
        public void testMasterViaAPI() throws IOException {
          Label label = Label.get("master");
          assertEquals("master", label.getName());
          FreeStyleProject project = j.createFreeStyleProject();
          project.save();
          project.setAssignedLabel(label);
          assertEquals("master", project.getAssignedLabel().getName());
          
          // value is null:
          assertEquals("master", project.getAssignedLabelString());
        }
      
        /**
         * Iff parsing works properly 
         */
        @Test
        public void testParseExpressionIff() throws IOException, ANTLRException {
          Label label = Label.parseExpression("a<->b");
          assertEquals(Iff.class, label.getClass());
          assertEquals("a<->b", label.getName());
          
          label = Label.parseExpression("a <-> b");
          assertEquals(Iff.class, label.getClass());
          assertEquals("a<->b", label.getName());
        }
        
        /**
         * Implies parsing fails with Exception. "a->b" cannot be parsed (see labelB).
         * On the other hand Implies.getName() returns exactly this String for a valid
         * instance. See labelA parsed from "a -> b" with blanks (which works).
         */
        @Test
        public void testParseExpressionImplies() throws IOException, ANTLRException {
          //This one works
          Label labelA = Label.parseExpression("a -> b");
          assertEquals(Implies.class, labelA.getClass());
          assertEquals("a->b", labelA.getName());
          
          //TokenStreamRecognitionException: line 1:3: unexpected char: '>'
          //at hudson.model.labels.LabelExpressionLexer.nextToken(LabelExpressionLexer.java:127)
          Label labelB = Label.parseExpression("a->b");
          
          assertEquals(Implies.class, labelB.getClass());
          assertEquals("a->b", labelB.getName());
          
        }
        
        /**
         * Setting the Implies expression without blanks does not work at all
         * (see {@link #testParseExpressionImplies}).
         * Setting it with blanks works, but after saving the job the result
         * is a LabelAtom instead of a LabelExpression!
         */
        @Test
        public void testImpliesViaAPI() throws IOException, ANTLRException {
          Label label = Label.parseExpression("a -> b");
          assertEquals(Implies.class, label.getClass());
          assertEquals("a->b", label.getName());
          FreeStyleProject project = j.createFreeStyleProject();
          project.setAssignedLabel(label);
          project.save();
      
          assertEquals("a->b", project.getAssignedLabel().getName());
          assertEquals("\"a->b\"", project.getAssignedLabelString());
      
          //AssertionError: expected:<class hudson.model.labels.LabelExpression$Implies> 
          //                 but was:<class hudson.model.labels.LabelAtom>
          assertEquals(Implies.class, project.getAssignedLabel().getClass());
        }
      
        /**
         * Setting the Implies expression in the UI (correctly) without blanks works, but the result
         * is a LabelAtom instead of a LabelExpression!
         */
        @Test
        public void testImpliesViaUI() throws Exception {
          FreeStyleProject project = j.createFreeStyleProject();
          setViaReflection(project, AbstractProject.class, "assignedNode", "a->b");
          project.save();
          
          assertEquals("a->b", project.getAssignedLabel().getName());
          assertEquals("\"a->b\"", project.getAssignedLabelString());
      
          //AssertionError: expected:<class hudson.model.labels.LabelExpression$Implies> 
          //                 but was:<class hudson.model.labels.LabelAtom>
          assertEquals(Implies.class, project.getAssignedLabel().getClass());
        }
      
        /**
         * Saving the Implies expression with blanks in the UI works properly.
         */
        @Test
        public void testImpliesViaUIWithBlanks() throws Exception {
          FreeStyleProject project = j.createFreeStyleProject();
          setViaReflection(project, AbstractProject.class, "assignedNode", "a -> b");
          project.save();
          
          assertEquals(Implies.class, project.getAssignedLabel().getClass());
          assertEquals("a->b", project.getAssignedLabel().getName());
          assertEquals("a -> b", project.getAssignedLabelString());
        }
      
        private void setViaReflection(Object object, Class<?> clazz, String fieldName, Object value) throws Exception
        {
          Field field = clazz.getDeclaredField(fieldName);
          field.setAccessible(true);
          field.set(object, value);
        }
        
      }
      

            wfollonier Wadeck Follonier
            alexanderlink alexanderlink
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: