Back to Repositories

Validating CLI Token Parsing and Pipe Character Handling in Alibaba Arthas

This test suite validates the CLI token implementation in Arthas, focusing on pipe character handling and command parsing functionality. It ensures proper tokenization of command-line inputs with various pipe character scenarios and regex patterns.

Test Coverage Overview

The test suite provides comprehensive coverage of CLI token parsing scenarios:

  • Supported pipe character usage without regex patterns
  • Pipe character handling with regex expressions
  • Unsupported pipe character combinations
  • Edge cases in command tokenization

Implementation Analysis

The testing approach uses JUnit to validate the CliTokenImpl class functionality. It employs systematic test cases to verify token parsing behavior in different command contexts, with particular attention to pipe character handling in both regex and non-regex scenarios.

The implementation uses pattern-based testing with clearly defined expected outcomes for each test case.

Technical Details

Testing infrastructure includes:

  • JUnit 4 testing framework
  • Custom token comparison logic
  • Helper methods for token list manipulation
  • Systematic test case organization with detailed documentation

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Comprehensive documentation of test scenarios
  • Clear separation of test cases by functionality
  • Robust validation of expected vs actual results
  • Efficient test helper methods for common operations

alibaba/arthas

core/src/test/java/com/taobao/arthas/core/shell/cli/impl/CliTokenImplTest.java

            
package com.taobao.arthas.core.shell.cli.impl;

import com.taobao.arthas.core.shell.cli.CliToken;
import org.junit.Assert;
import org.junit.Test;

import java.util.Iterator;
import java.util.List;

public class CliTokenImplTest {

    /**
     * supported:
     * <p>
     * case1:
     * thread| grep xxx
     * [thread|, grep, xxx] -> [thread, |, grep, xxx]
     * case:2
     * thread | grep xxx
     * [thread, |, grep, xxx] -> [thread, |, grep, xxx]
     * case3:
     * thread |grep xxx
     * [thread, |grep] -> [thread, |, grep, xxx]
     */
    @Test
    public void testSupportedPipeCharWithoutRegex() {
        String[] expectedTextTokenValue = new String[]{"thread", "|", "grep", "xxx"};
        List<CliToken> actualTokens = CliTokenImpl.tokenize("thread| grep xxx");
        assertEquals(expectedTextTokenValue, actualTokens);

        actualTokens = CliTokenImpl.tokenize("thread | grep xxx");
        assertEquals(expectedTextTokenValue, actualTokens);

        actualTokens = CliTokenImpl.tokenize("thread |grep xxx");
        assertEquals(expectedTextTokenValue, actualTokens);
    }

    /**
     * supported:
     * <p>
     * case1:
     * trace -E classA|classB methodA|methodB| grep classA
     * [trace, -E, classA|classB, methodA|methodB|, grep, classA] -> [trace, -E, classA|classB, methodA|methodB, |, grep, classA]
     * case2:
     * trace -E classA|classB methodA|methodB | grep classA
     * [trace, -E, classA|classB, methodA|methodB, |, grep, classA] -> [trace, -E, classA|classB, methodA|methodB, |, grep, classA]
     * case3:
     * trace -E classA|classB methodA|methodB |grep classA
     * [trace, -E, classA|classB, methodA|methodB, |grep, classA] -> [trace, -E, classA|classB, methodA|methodB, |, grep, classA]
     */
    @Test
    public void testSupportedPipeCharWithRegex() {
        String[] expectedTextTokenValue = new String[]{"trace", "-E", "classA|classB", "methodA|methodB", "|", "grep", "classA"};
        List<CliToken> actualTokens = CliTokenImpl.tokenize("trace -E classA|classB methodA|methodB| grep classA");
        assertEquals(expectedTextTokenValue, actualTokens);

        actualTokens = CliTokenImpl.tokenize("trace -E classA|classB methodA|methodB | grep classA");
        assertEquals(expectedTextTokenValue, actualTokens);

        actualTokens = CliTokenImpl.tokenize("trace -E classA|classB methodA|methodB |grep classA");
        assertEquals(expectedTextTokenValue, actualTokens);
    }

    /**
     * unsupported:
     * <p>
     * case1:
     * thread|grep xxx
     * [thread|grep, xxx] -> [thread|grep, xxx]
     * case2:
     * trace -E classA|classB methodA|methodB|grep classA
     * [trace, -E, classA|classB, methodA|methodB|grep, classA] -> [trace, -E, classA|classB, methodA|methodB|grep, classA]
     * case3:
     * trace -E classA|classB| methodA|methodB | grep classA
     * [trace, -E, classA|classB|, methodA|methodB, |, grep, classA] -> [trace, -E, classA|classB,|, methodA|methodB, |, grep, classA]
     */
    @Test
    public void testUnSupportedPipeChar() {
        String[] expectedTextTokenValue = new String[]{"thread|grep", "xxx"};
        List<CliToken> actualTokens = CliTokenImpl.tokenize("thread|grep xxx");
        assertEquals(expectedTextTokenValue, actualTokens);

        expectedTextTokenValue = new String[]{"trace", "-E", "classA|classB", "methodA|methodB|grep", "classA"};
        actualTokens = CliTokenImpl.tokenize("trace -E classA|classB methodA|methodB|grep classA");
        assertEquals(expectedTextTokenValue, actualTokens);

        expectedTextTokenValue = new String[]{"trace", "-E", "classA|classB", "|", "methodA|methodB", "|", "grep", "classA"};
        actualTokens = CliTokenImpl.tokenize("trace -E classA|classB| methodA|methodB | grep classA");
        assertEquals(expectedTextTokenValue, actualTokens);
    }

    private void assertEquals(String[] expectedTextTokenValue, List<CliToken> actualTokens) {
        removeBlankToken(actualTokens);
        for (int i = 0; i < expectedTextTokenValue.length; i++) {
            Assert.assertEquals(expectedTextTokenValue[i], actualTokens.get(i).value());
        }
    }

    private void removeBlankToken(List<CliToken> cliTokens) {
        CliToken blankToken = new CliTokenImpl(false, " ");
        Iterator<CliToken> it = cliTokens.iterator();
        while (it.hasNext()) {
            CliToken token = it.next();
            if (blankToken.equals(token)) {
                it.remove();
            }
        }
    }

}