论文给出例子实现中遇到问题

虽然论文已经写得挺明白的了(老师学长如是说道),但是本垃圾还是看得五步一百度,十步一谷歌…

菜死了,理解速度还跟不上,有时候一段文字需要查好几个小时才看明白…

XML格式

XML格式是一种用语传输以及存储数据的文件格式,特点如下:

  • 纯文本,使用UTF-8编码
  • 可嵌套,可以充分表示结构化的数据(可以理解为树状结构)

固定结构

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE note SYSTEM "book.dtd">
<book id="1">
    <name>Java核心技术</name>
    <author>Cay S. Horstmann</author>
    <isbn lang="CN">1234567</isbn>
    <tags>
        <tag>Java</tag>
        <tag>Network</tag>
    </tags>
    <pubDate/>
</book>
  • 前两行 version encoding dtd(文档定义类型Document Type Definition)都是可以选择的参数
  • book标签为文档的根节点,一般来说根节点都只有一个
  • 格式要求完全正确才能被解析,任何没有正确嵌套的标签都会导致错误

DTD文档

上文中的第二行,是把这个xml文件交给了book.dtd文档来验证规则,在DTD文档的内部,可以定义一些xml需要满足的规则,如果不满足就会判定为xml数据结构不符要求。如,可以要求根元素必须是book,还可以要求isbn元素必须有lang元素,甚至可以要求book下的子元素必须含有某些指定元素。

转义字符

由于文档格式中大量使用了一些字符,所以…

字符表示
<&It;
>&gt;
&&amp;
"&quot;
'&apos;

相关拓展

  • DTD和XSD:验证XML结构和数据是否有效;
  • Namespace:XML节点和属性的名字空间;
  • XSLT:把XML转化为另一种文本;
  • XPath:一种XML节点查询语言;

DOM API

DOM将xml文档一次读入,并把内容转换为树状结构放在内存中,在后期调用中十分方便,但是极为消耗内存。对应的也有以流形式读取xml的SAX API,通过一些些奇奇怪怪的方式进行使用(事件回调?)

InputStream input = Main.class.getResourceAsStream("/book.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(input);

通过对doc的调用就可以进行读取xml中的数据,调用printNode(doc, 0)

void printNode(Node n, int indent) { // 输出xml的内容
    for (int i = 0; i < indent; i++) { // 打出缩进
        System.out.print(' ');
    }
    switch (n.getNodeType()) {
    case Node.DOCUMENT_NODE: // Document节点
        System.out.println("Document: " + n.getNodeName());
        break;
    case Node.ELEMENT_NODE: // 元素节点
        System.out.println("Element: " + n.getNodeName());
        break;
    case Node.TEXT_NODE: // 文本
        System.out.println("Text: " + n.getNodeName() + " = " + n.getNodeValue());
        break;
    case Node.ATTRIBUTE_NODE: // 属性
        System.out.println("Attr: " + n.getNodeName() + " = " + n.getNodeValue());
        break;
    default: // 其他
        System.out.println("NodeType: " + n.getNodeType() + ", NodeName: " + n.getNodeName());
    }
    for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) {
        printNode(child, indent + 1);
    }
}

单元测试 Assume 与 Assert

JUnit 单元测试

JUnit会吧带有@Test的方法识别为测试方法,测试类一般明明为**Test,测试方法一般命名为test**。常用断言方法如下:

  • assertTrue(): 期待结果为true
  • assertFalse(): 期待结果为false
  • assertNotNull(): 期待结果为非null
  • assertArrayEquals(): 期待结果为数组并与期望数组每个元素的值均相等

导入JUnit之后,可以在Eclipse中快速完成单元测试,具体内容

Assume

对方法的参数进行合法性校验,如果校验不合格则直接抛异常,而不执行测试,默认的BlockJUnit4ClassRunner及其子类会捕获这个异常并跳过当前测试,如果使用自定义的Runner则无法保证行为,视Runner的实现而定。

如果有的时候必须规定具备某个条件才允许测试,但又不判断为fail,则可以使用:

@Before  
public void setUp() {  
	String versionNumber = "7"; //Get it from configuration on runtime  
	Assume.assumeTrue(Integer.valueOf(versionNumber) == 7);  
}  
  
@Test  
public void testIfVersioonGreaterThan4() {  
	System.out.println("Test executed");  
}  

当检查versionNumber不是7的时候,跳过此次测试,并throw一个可被捕获的异常。

Tutorial 1

预设问题

闰年判定

在400年闰年判定上预设问题。

时间比较

没有考虑时区,直接比较了时间。

生成器

JQF利用junit-quickcheck的框架生成结构化输入,为了生成T类型的输入,就必须要有一个Generator<T>的拓展类,生成这样的拓展类需要一些生成方法,也就是下文代码中的T_Generator的方法,然后就可以通过拓展类批量生成内容参与测试。

import java.util.GregorianCalendar;
import java.util.TimeZone;

import com.pholser.junit.quickcheck.generator.GenerationStatus;
import com.pholser.junit.quickcheck.generator.Generator;
import com.pholser.junit.quickcheck.random.SourceOfRandomness;
// 以上三段代码需要后面仔细读一下

import static java.util.GregorianCalendar.*; // 导入GregorianCalendar的静态变量

public class CalendarGenerator extends Generator<GregorianCalendar> {
    // 继承 GregorianCalendar 的构造方法
    public CalendarGenerator() {
        super(GregorianCalendar.class); 
    }

    // 生成一个随机的时间用于测试
    @Override
    public GregorianCalendar generate(SourceOfRandomness random, GenerationStatus __ignore__) {
        // cal 实例存储随机生成的时间
        GregorianCalendar cal = new GregorianCalendar();
        cal.setLenient(true); // 允许了时间的自动跳转,即可以从四月三十一号自动转换为五月一号

        // 随机生成日期
        cal.set(DAY_OF_MONTH, random.nextInt(31) + 1); 
        cal.set(MONTH, random.nextInt(12) + 1); 
        cal.set(YEAR, random.nextInt(cal.getMinimum(YEAR), cal.getMaximum(YEAR)));

        // 随机生成时间
        if (random.nextBoolean()) {
            cal.set(HOUR, random.nextInt(24));
            cal.set(MINUTE, random.nextInt(60));
            cal.set(SECOND, random.nextInt(60));
        }
        
        // 随机生成时区
        String[] allTzIds = TimeZone.getAvailableIDs(); // 获取时区列表(e.g. "America/Los_Angeles")
        String tzId = random.choose(allTzIds); // 随机抽取一个时区
        TimeZone tz = TimeZone.getTimeZone(tzId); // 把String时区转换成时区对象
        cal.setTimeZone(tz); 
        
        return cal;
    }
}

Test代码

import java.util.*;
import static java.util.GregorianCalendar.*;
import static org.junit.Assert.*;
import static org.junit.Assume.*;

// 感觉以下的内容好像都要以后读一读,感觉用起来不明不白的
import org.junit.runner.RunWith;
import com.pholser.junit.quickcheck.*;
import com.pholser.junit.quickcheck.generator.*;
import edu.berkeley.cs.jqf.fuzz.*;

@RunWith(JQF.class) // 告知JUnit使用JQF来进行测试
public class CalendarTest {

    @Fuzz // 标记测试用的代码,方便JQF找到,下文FROM标记了测试数据的来源
    public void testLeapYear(@From(CalendarGenerator.class) GregorianCalendar cal) {
        // 筛选掉那些不是二月29的时间,这样留下的日期都是闰年了
        assumeTrue(cal.get(MONTH) == FEBRUARY);
        assumeTrue(cal.get(DAY_OF_MONTH) == 29);
        assertTrue(cal.get(YEAR) + " should be a leap year", CalendarLogic.isLeapYear(cal));
    }

    @Fuzz
    public void testCompare(@Size(max=100) List<@From(CalendarGenerator.class) GregorianCalendar> cals) {
        Collections.sort(cals, CalendarLogic::compare); // 调用有预设问题的逻辑

        // 根据预设好的逻辑,应该是升序排序
        for (int i = 1; i < cals.size(); i++) {
            Calendar c1 = cals.get(i-1);
            Calendar c2 = cals.get(i);
            assumeFalse(c1.equals(c2)); // 排除掉相等的情况
            assertTrue(c1 + " should be before " + c2, c1.before(c2));  // 通过正确的方法检验
        }
    }
}

比较迷惑的是文档中在最后一段的话,为什么这个From的标签没有给List而是给了List的元素,我理解的是,元素才是由CalendarGenerator.class生成的,而把这个元素放到List中进行调用的话,在它Generator的父类中就能自动生成一个包含这些生成元素的List,而Size标签就是为了限制这个随机生成的List大小的。

编译内容与模糊测试及结果

Javac 编译指令参数 -cp

如果你这个java文件中,引入其他的jar包,需要用到 -cp 参数,全称是classpath

javac -cp .:$(/path/to/jqf/scripts/classpath.sh) CalendarLogic.java CalendarGenerator.java CalendarTest.java

对于.:$(/path/to/jqf/scripts/classpath.sh)解释:

  • $(): 执行括号中的内容,然后将执行所得结果填充到该位置
  • JQF/scripts/classpath.sh是获取当前JQF所依赖的所有JAR包
  • : 并列当前目录下的和classpath.sh获取的内容

通过JUnit和QuickCheck进行测试

java -cp .:$(/path/to/jqf/scripts/classpath.sh) org.junit.runner.JUnitCore CalendarTest

-cp参数的意义同上,但是这次是运行过程中依赖的jar,意义上也许略有不同?

可能会获得错误消息Assumption is too strong; too many inputs discarded(反正我试了几次没试出来),原因是随机生成的时间落到闰年的二月二十九的概率太小了,可能结束之后都找不到一个能通过Assume的数据…(好惨)

命令行开启模糊测试

/path/to/jqf/bin/jqf-zest -c .:$(/path/to/jqf/scripts/classpath.sh) CalendarTest testLeapYear

这是调用了jqf-zest,-c参数是提供了配置类的路径,配置类还是从classpath.sh中获取。通过testLeapYear方法,测试了Calendartest的内容,命令行看起来比较混乱,以后还是用mvn插件吧…运行起来之后如下图,加一些注释

Zest: Validity Fuzzing with Parametric Generators
-------------------------------------------------
# 通过通过 testLeapYear 方法,测试了 Calendartest 的内容
Test name:            CalendarTest#testLeapYear
# 模糊测试结果的保存路径,路径下既有成功的数据,也有失败的的数据
Results directory:    /path/to/tutorial/fuzz-results
# 模糊测试时长,可以在mvn插件中指定测试时长
Elapsed time:         5s (no time limit)
# 已经进行德测试数量,即总投入的数据量
Number of executions: 5,856
# 总量中有效的数据以及占比,因为那个闰年的条件太苛刻,所以有效比看起来不太高
Valid inputs:         271 (4.63%)
Cycles completed:     4
# 导致错误的输入的数量(可能导致的错误都是同一种类型)
Unique failures:      1
# 模糊测试中S集的大小
Queue size:           3 (3 favored last cycle)
Current parent input: 0 (favored) {240/240 mutations}
Execution speed:      1,300/sec now | 1,098/sec overall
# 覆盖率
Total coverage:       8 (0.01% of map)
Valid coverage:       6 (0.01% of map)

命令行查看模糊测试结果

/path/to/jqf/bin/jqf-repro -c .:$(/path/to/jqf/scripts/classpath.sh) CalendarTest testLeapYear 

具体内容的解释和上文一样,不做注解了

Tutorial 2

上一个Tutorial使用的命令行操作JQF+Zest的工具,这个用了更加简洁的mvn插件。

依赖以及插件配置(通过pom.xml)

调用插件的话,需要把测试用的文件以及生成器文件放在一个maven的框架中,并在maven框架的pom.xml中增加插件,具体信息如下。

<project xmlns="http://maven.apache.org/POM/4.0.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>examples</groupId>
    <artifactId>zest-tutorial</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- Google Closure: 被检测的文件 -->
        <dependency>
            <groupId>com.google.javascript</groupId>
            <artifactId>closure-compiler</artifactId>
            <version>v20180204</version>
            <!-- 使用阶段:test:仅参与测试相关的内容,包括测试的编译和执行 -->
            <scope>test</scope>
        </dependency>

        <!-- JQF: @Fuzz标签要用到的依赖 -->
        <dependency>
            <groupId>edu.berkeley.cs.jqf</groupId>
            <artifactId>jqf-fuzz</artifactId>
            <!-- 确保安装最新版 https://mvnrepository.com/artifact/edu.berkeley.cs.jqf -->
            <version>1.1</version> 
            <scope>test</scope>
        </dependency>

        <!-- JUnit-QuickCheck: 编写生成器要用到的API -->
        <dependency>
            <groupId>com.pholser</groupId>
            <artifactId>junit-quickcheck-generators</artifactId>
            <version>0.8</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <!-- 安装mvn调用jqf-zest的插件 -->
    <build>
        <plugins>
            <plugin>
                <groupId>edu.berkeley.cs.jqf</groupId>
                <artifactId>jqf-maven-plugin</artifactId>
                <!-- 确保安装最新版 https://mvnrepository.com/artifact/edu.berkeley.cs.jqf -->
                <version>1.1</version>
            </plugin>
        </plugins>
    </build>
</project>

Test代码

内容保存到src/test/java/examples/CompilerTest.java,即maven的资源文件下,用于test的、java语言的、examples包。

package examples;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

// 以下这些应该时被测试的内容,需要调用这些内容然后检查是否正确
import com.google.javascript.jscomp.CompilationLevel;
import com.google.javascript.jscomp.Compiler;
import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.Result;
import com.google.javascript.jscomp.SourceFile;
// 以下这些应该是和@出的标签配套的内容(猜测)
import com.pholser.junit.quickcheck.From;
import edu.berkeley.cs.jqf.fuzz.Fuzz;
import edu.berkeley.cs.jqf.fuzz.JQF;
import org.junit.Before;
import org.junit.runner.RunWith;

import static org.junit.Assume.*;

@RunWith(JQF.class)
public class CompilerTest {
    static {
        // 禁用所有因为Closure运行导致的日志读写操作,这样可以加快测试速度
        java.util.logging.LogManager.getLogManager().reset();
    }

    // 用到的的编译器
    private Compiler compiler = new Compiler(new PrintStream(new ByteArrayOutputStream(), false));
    // 可供修改的默认编译选项
    private CompilerOptions options = new CompilerOptions();
    // 这个是啥???
    private SourceFile externs = SourceFile.fromCode("externs", "");

    @Before // 以下方法为在测试开始前运行一次的内容
    public void initCompiler() {
        // 禁用多线程以及过程信息打印
        compiler.disableThreads();
        options.setPrintConfig(false);
        // 开启所有的安全优化(为啥???)
        CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
    }

    // 进行编译并返回编译结果(期待在这个过程中发现未知的错误)
    private Result compile(SourceFile input) {
        Result result = compiler.compile(externs, input, options);
        assumeTrue(result.success); // 这里看不懂了,为啥要有这个assume???
        return result;
    }

    // 调用已有的默认的Generator,生成随机的字符串来进行测试(未结构化)
    @Fuzz
    public void testWithString(String code) {
        SourceFile input = SourceFile.fromCode("input", code);
        // 下句并未使用断言,想获取在编译过程中出乎意料的报错,而非我们可以预想到的问题
        compile(input);
    }
}

生成器代码

package examples;

import java.util.*;
import java.util.function.*;

import com.pholser.junit.quickcheck.generator.GenerationStatus;
import com.pholser.junit.quickcheck.generator.Generator;
import com.pholser.junit.quickcheck.random.SourceOfRandomness;

/* Generates random strings that are syntactically valid JavaScript */
public class JavaScriptCodeGenerator extends Generator<String> {
    public JavaScriptCodeGenerator() {
        super(String.class); // Register type of generated object
    }

    private GenerationStatus status; // saved state object when generating
    private static final int MAX_IDENTIFIERS = 100;
    private static final int MAX_EXPRESSION_DEPTH = 10;
    private static final int MAX_STATEMENT_DEPTH = 6;
    private static Set<String> identifiers; // Stores generated IDs, to promote re-use
    private int statementDepth; // Keeps track of how deep the AST is at any point
    private int expressionDepth; // Keeps track of how nested an expression is at any point

    private static final String[] UNARY_TOKENS = {
            "!", "++", "--", "~",
            "delete", "new", "typeof"
    };

    private static final String[] BINARY_TOKENS = {
            "!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+", "+=", ",",
            "-", "-=", "/", "/=", "<", "<<", ">>=", "<=", "=", "==", "===",
            ">", ">=", ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||",
            "in", "instanceof"
    };

    /** Main entry point. Called once per test case. Returns a random JS program. */
    @Override
    public String generate(SourceOfRandomness random, GenerationStatus status) {
        this.status = status; // we save this so that we can pass it on to other generators
        this.identifiers = new HashSet<>();
        this.statementDepth = 0;
        this.expressionDepth = 0;
        return generateStatement(random).toString();
    }

    /** Utility method for generating a random list of items (e.g. statements, arguments, attributes) */
    private static List<String> generateItems(Function<SourceOfRandomness, String> genMethod, SourceOfRandomness random,
                                             int mean) {
        int len = random.nextInt(mean*2); // Generate random number in [0, mean*2) 
        List<String> items = new ArrayList<>(len);
        for (int i = 0; i < len; i++) {
            items.add(genMethod.apply(random));
        }
        return items;
    }

    /** Generates a random JavaScript statement */
    private String generateStatement(SourceOfRandomness random) {
        statementDepth++;
        String result;
        // If depth is too high, then generate only simple statements to prevent infinite recursion
        // If not, generate simple statements after the flip of a coin
        if (statementDepth >= MAX_STATEMENT_DEPTH || random.nextBoolean()) {
            // Choose a random private method from this class, and then call it with `random`
            result = random.choose(Arrays.<Function<SourceOfRandomness, String>>asList(
                    this::generateExpressionStatement,
                    this::generateBreakNode,
                    this::generateContinueNode,
                    this::generateReturnNode,
                    this::generateThrowNode,
                    this::generateVarNode,
                    this::generateEmptyNode
            )).apply(random);
        } else {
            // If depth is low and we won the flip, then generate compound statements
            // (that is, statements that contain other statements)
            result = random.choose(Arrays.<Function<SourceOfRandomness, String>>asList(
                    this::generateIfNode,
                    this::generateForNode,
                    this::generateWhileNode,
                    this::generateNamedFunctionNode,
                    this::generateSwitchNode,
                    this::generateTryNode,
                    this::generateBlock
            )).apply(random);
        }
        statementDepth--; // Reset statement depth when going up the recursive tree
        return result;
    }

    /** Generates a random JavaScript expression using recursive calls */
    private String generateExpression(SourceOfRandomness random) {
        expressionDepth++;
        String result;
        // Choose terminal if nesting depth is too high or based on a random flip of a coin
        if (expressionDepth >= MAX_EXPRESSION_DEPTH || random.nextBoolean()) {
            result = random.choose(Arrays.<Function<SourceOfRandomness, String>>asList(
                    this::generateLiteralNode,
                    this::generateIdentNode
            )).apply(random);
        } else {
            // Otherwise, choose a non-terminal generating function
            result = random.choose(Arrays.<Function<SourceOfRandomness, String>>asList(
                    this::generateBinaryNode,
                    this::generateUnaryNode,
                    this::generateTernaryNode,
                    this::generateCallNode,
                    this::generateFunctionNode,
                    this::generatePropertyNode,
                    this::generateIndexNode,
                    this::generateArrowFunctionNode
            )).apply(random);
        }
        expressionDepth--;
        return "(" + result + ")";
    }

    /** Generates a random binary expression (e.g. A op B) */
    private String generateBinaryNode(SourceOfRandomness random) {
        String token = random.choose(BINARY_TOKENS); // Choose a binary operator at random
        String lhs = generateExpression(random);
        String rhs = generateExpression(random);

        return lhs + " " + token + " " + rhs;
    }

    /** Generates a block of statements delimited by ';' and enclosed by '{' '}' */
    private String generateBlock(SourceOfRandomness random) {
        return "{ " + String.join(";", generateItems(this::generateStatement, random, 4)) + " }";
    }

    private String generateBreakNode(SourceOfRandomness random) {
        return "break";
    }

    private String generateCallNode(SourceOfRandomness random) {
        String func = generateExpression(random);
        String args = String.join(",", generateItems(this::generateExpression, random, 3));

        String call = func + "(" + args + ")";
        if (random.nextBoolean()) {
            return call;
        } else {
            return "new " + call;
        }
    }

    private String generateCaseNode(SourceOfRandomness random) {
        return "case " + generateExpression(random) + ": " +  generateBlock(random);
    }

    private String generateCatchNode(SourceOfRandomness random) {
        return "catch (" + generateIdentNode(random) + ") " +
                generateBlock(random);
    }

    private String generateContinueNode(SourceOfRandomness random) {
        return "continue";
    }

    private String generateEmptyNode(SourceOfRandomness random) {
        return "";
    }

    private String generateExpressionStatement(SourceOfRandomness random) {
        return generateExpression(random);
    }

    private String generateForNode(SourceOfRandomness random) {
        String s = "for(";
        if (random.nextBoolean()) {
            s += generateExpression(random);
        }
        s += ";";
        if (random.nextBoolean()) {
            s += generateExpression(random);
        }
        s += ";";
        if (random.nextBoolean()) {
            s += generateExpression(random);
        }
        s += ")";
        s += generateBlock(random);
        return s;
    }

    private String generateFunctionNode(SourceOfRandomness random) {
        return "function(" + String.join(", ", generateItems(this::generateIdentNode, random, 5)) + ")" + generateBlock(random);
    }

    private String generateNamedFunctionNode(SourceOfRandomness random) {
        return "function " + generateIdentNode(random) + "(" + String.join(", ", generateItems(this::generateIdentNode, random, 5)) + ")" + generateBlock(random);
    }

    private String generateArrowFunctionNode(SourceOfRandomness random) {
        String params = "(" + String.join(", ", generateItems(this::generateIdentNode, random, 3)) + ")";
        if (random.nextBoolean()) {
            return params + " => " + generateBlock(random);
        } else {
            return params + " => " + generateExpression(random);
        }

    }

    private String generateIdentNode(SourceOfRandomness random) {
        // Either generate a new identifier or use an existing one
        String identifier;
        if (identifiers.isEmpty() || (identifiers.size() < MAX_IDENTIFIERS && random.nextBoolean())) {
            identifier = random.nextChar('a', 'z') + "_" + identifiers.size();
            identifiers.add(identifier);
        } else {
            identifier = random.choose(identifiers);
        }

        return identifier;
    }

    private String generateIfNode(SourceOfRandomness random) {
        return "if (" +
                generateExpression(random) + ") " +
                generateBlock(random) +
                (random.nextBoolean() ? generateBlock(random) : "");
    }

    private String generateIndexNode(SourceOfRandomness random) {
        return generateExpression(random) + "[" + generateExpression(random) + "]";
    }

    private String generateObjectProperty(SourceOfRandomness random) {
        return generateIdentNode(random) + ": " + generateExpression(random);
    }

    private String generateLiteralNode(SourceOfRandomness random) {
        // If we are not too deeply nested, then it is okay to generate array/object literals
        if (expressionDepth < MAX_EXPRESSION_DEPTH && random.nextBoolean()) {
            if (random.nextBoolean()) {
                // Array literal
                return "[" + String.join(", ", generateItems(this::generateExpression, random, 3)) + "]";
            } else {
                // Object literal
                return "{" + String.join(", ", generateItems(this::generateObjectProperty, random, 3)) + "}";

            }
        } else {
            // Otherwise, generate primitive literals
            return random.choose(Arrays.<Supplier<String>>asList(
                    () -> String.valueOf(random.nextInt(-10, 1000)), // int literal
                    () -> String.valueOf(random.nextBoolean()),      // bool literal
                    () -> generateStringLiteral(random),
                    () -> "undefined",
                    () -> "null",
                    () -> "this"
            )).get();
        }
    }

    private String generateStringLiteral(SourceOfRandomness random) {
        // Generate an arbitrary string using the default string generator, and quote it
        return '"' + gen().type(String.class).generate(random, status) + '"';
    }

    private String generatePropertyNode(SourceOfRandomness random) {
        return generateExpression(random) + "." + generateIdentNode(random);
    }

    private String generateReturnNode(SourceOfRandomness random) {
        return random.nextBoolean() ? "return" : "return " + generateExpression(random);
    }

    private String generateSwitchNode(SourceOfRandomness random) {
        return "switch(" + generateExpression(random) + ") {"
                + String.join(" ", generateItems(this::generateCaseNode, random, 2)) + "}";
    }

    private String generateTernaryNode(SourceOfRandomness random) {
        return generateExpression(random) + " ? " + generateExpression(random) +
                " : " + generateExpression(random);
    }

    private String generateThrowNode(SourceOfRandomness random) {
        return "throw " + generateExpression(random);
    }

    private String generateTryNode(SourceOfRandomness random) {
        return "try " + generateBlock(random) + generateCatchNode(random);
    }

    private String generateUnaryNode(SourceOfRandomness random) {
        String token = random.choose(UNARY_TOKENS);
        return token + " " + generateExpression(random);
    }

    private String generateVarNode(SourceOfRandomness random) {
        return "var " + generateIdentNode(random);
    }

    private String generateWhileNode(SourceOfRandomness random) {
        return "while (" + generateExpression(random) + ")" + generateBlock(random);
    }
}

在使用IDEA阅读代码过程中遇到的问题

can’t find the declaration to go to

问题背景

在使用IDEA过程中,需要通过Ctrl + click的操作去方便阅读代码的上下文,更快确定父类、库之类的东西。但是在直接导入JQF的过程中发现无法找到,总是返回如题的报错。

解决过程

上网搜索发现是IDEA的配置中没有配置好本地已有的maven插件,IDEA中的maven插件与本地的maven插件发生了冲突,在File - New Project Settings 中修改Build - Build Tools - Maven中修改就行,然后清除掉之前已有的问题文件,重新建立依赖关系,就能准确定位了。

在此过程中,需要注意IDEA的Open和Import是不同的功能,在从外部导入的文件(git下载的,svn下载的,直接拷贝的)建议直接使用Import,而已经在本地Import过的文件如果需要再次打开,则使用Open。