说明:只针对于groovy脚本,只能将两个脚本合并起来,不会进行去重,内容不会重复,然后也可以进行执行,验证脚本语法也没问题。2个以上也可以的,只是用了最笨的方法实现了需求。别去看怎么写的了,能套死你。而且脚本中仅支持以下注解,Test注解可以多个,其他的不行,没有那么的人性化,有啥问题可以问我。
"@BeforeProcess", "@BeforeThread", "@Before", "@Test
package org.ngrinder.script.controller;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.entity.ContentType;
import org.ngrinder.script.common.DataTypeConstant;
import org.ngrinder.script.common.FileConstant;
import org.ngrinder.script.common.FileStatusEnum;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.*;
/**
* 脚本处理工具类,目前只支持Groovy脚本
*
* @Author xiaoxin
* @Date 2021/8/24 14:49
* @Version 1.0
*/
@Slf4j
@Component
public class MergeFileUtil {
/**
* 读取文件,传过来的是多个文件的数组形式,读取到文件之后合并起来,返回最终合并的文件
* 1.获取单个文件
* 2.获取import字符串
* 3.获取文件中的全局变量
* 4.获取文件中的代码片段
* 5.对变量和代码片段进行更换名称
* 6.存进集合并返回
* 7.取出方法内的代码片段
* 8.获取多多个文件的全部集合
* 9.进行合并为集合
* 10.创建文件,输入数据
*/
public MultipartFile readFile(List<Map<String, String>> file) throws FileException, Exception {
String str;
//代码文本拼接
StringBuffer sb = new StringBuffer();
//开头是import的集合
List<String> startList = new ArrayList<>();
//开头是public修饰符的代码文本行
List<String> pulblicGlobalList = new ArrayList<>();
//全局变量集合
List<String> globalList = new ArrayList<>();
//代码片段集合
List<Map<String, List<String>>> codeList = new ArrayList<>();
//更改过的全局变量集合、代码片段集合
Map<String, List<List<Map<String, String>>>> replaceFile;
//合并文本集合
List<Map<String, List<List<Map<String, String>>>>> mergeList = new ArrayList<>();
//判断文件名是否重复
// if (!judgeName(file)) {
// throw new FileException(103,"文件名字重复");
// }
if (file.get(0).keySet().size() == 1) {
throw new FileException(FileStatusEnum.UP_TO_TWO.getCode(), FileStatusEnum.UP_TO_TWO.getMsg());
}
if (file.get(0).keySet().size() > 3) {
throw new FileException(FileStatusEnum.UP_TO_TREE.getCode(), FileStatusEnum.UP_TO_TREE.getMsg());
}
Set<String> strings = file.get(0).keySet();
for (String s : strings) {
//获取import开头的
startList = getImportStr(file.get(0).get(s));
//获取去public开头的
pulblicGlobalList = getPublicStr(file.get(0).get(s));
//获取全局变量集合
List<String> GVList = getOverAll(importStart(pulblicGlobalList), globalList);
Map<String, List<String>> kvHashMap = new HashMap<>();
List<String> objects1 = new ArrayList<>();
//获取注解的代码文本集合
List<String> ANList = getAnnotation(file.get(0).get(s));
for (int j = 0; j < ANList.size(); j++) {
objects1.add(ANList.get(j));
kvHashMap.put(String.valueOf(j), objects1);
}
codeList.add(kvHashMap);
//通过全局变量和文本获取修改过名称的全局变量集合、代码片段集合
replaceFile = replaceFile(s, GVList, objects1);
mergeList.add(replaceFile);
}
//调用合并方法,返回真正的file文件
MultipartFile multipartFile = mergeFileCode(mergeList, startList);
return multipartFile;
}
private List<String> getOverAll(List<String> pulblicGlobalList, List<String> globalList) {
for (String a : pulblicGlobalList) {
if (globalObject(a)) {
//判断尾部,如果尾部没有分号,则自动追加一个分号,为了后面的步骤流程
String substring = a.substring(a.length() - 1);
if (!substring.equals(FileConstant.BRANCH)) {
a = a + FileConstant.BRANCH;
}
globalList.add(a);
}
}
return globalList;
}
/**
* 判断字符串开头是否是import
*
* @param str
* @return
*/
public boolean decideStart(String str) {
boolean flag = false;
if (str.indexOf(FileConstant.IMPORT) == 0) {
flag = true;
}
return flag;
}
/**
* 判断字符串开头是否以public
*
* @param str
* @return
*/
public boolean publicStart(String str) {
boolean flag = false;
//对字符串去除前后空格
String trim = str.trim();
if (trim.indexOf(FileConstant.PUBLIC) == 0) {
flag = true;
}
return flag;
}
/**
* 暂时弃用,因为目前groovy脚本很多尾部没有分号
* 判断字符串结尾是否符合规则
*
* @param str
* @return
*/
public boolean decideEnding(String str) {
boolean flag = false;
String substring = str.substring(str.length());
if (substring.equals(FileConstant.BRANCH)) {
flag = true;
}
return flag;
}
/**
* 获取全局变量
*
* @param str
* @return
*/
public boolean globalObject(String str) {
boolean flag = false;
String s = str.substring(str.length() - 1);
if (!s.equals(FileConstant.BRACKETS_LEFT)) {
flag = true;
}
return flag;
}
/**
* 对顶部的import引入的文件文本进行筛选
* 使用hashSet去除相同的
*
* @param list
* @return
*/
public List<String> importStart(List list) {
HashSet h = new HashSet(list);
list.clear();
list.addAll(h);
return list;
}
/**
* 判断文件名字是否重复
*
* @param
* @return
*/
public boolean judgeName(List<Map<String, String>> file) {
List<String> list = new ArrayList<>();
for (int i = 0; i < file.size(); i++) {
Set<String> strings = file.get(i).keySet();
for (String s : strings) {
list.add(s);
}
}
HashSet<String> set = new HashSet<>(list);
Boolean result = set.size() == list.size() ? true : false;
return result;
}
/**
* 对代码块中的变量、代码文本进行名称替换
*
* @param constant 全局变量集合
* @return 修改过的file的全局变量集合、代码文本集合
* @methodCode 代码文本集合
*/
public Map<String, List<List<Map<String, String>>>> replaceFile(String fileName, List<String> constant, List<String> methodCode) {
//原数据
String originParam = null;
//克隆数据
String cloneParam = null;
//全局变量集合
List<String> globalList = new ArrayList<>();
//不符合条件的代码文本集合
List<String> nonConformity = new ArrayList<>();
//克隆数据集合
List<Map<String, String>> cloneParamList = new ArrayList<>();
//分割数组
String[] split;
//替换后的代码文本集合
List<String> methodCodeList = new ArrayList<>();
//返回值list
List<List<Map<String, String>>> returnList = new ArrayList<>();
//存放原始数据和克隆数据
Map<String, String> map = new HashMap<>();
Map<String, String> cloneParamMap = new HashMap<>();
//随机数
int randomNum = new Random().nextInt(100);
for (int t = 0; t < constant.size(); t++) {
split = constant.get(t).split("\\s+");
for (int i = 0; i < split.length; i++) {
//判断字符串中是否有 “=”等号
if (split[i].equals(FileConstant.EQUAL)) {
originParam = split[i - 1];
cloneParam = originParam + "_" + randomNum;
log.info("原始数据+克隆数据{}" + originParam + "----------" + cloneParam);
String replace = constant.get(t).replace(originParam, cloneParam);
cloneParamMap.put(replace, replace);
map.put(originParam, cloneParam);
globalList.add(originParam);
break;
}
//尾部是分号
if (split[i].indexOf(FileConstant.BRANCH) >= 0) {
String str = split[i].substring(split[i].length() - 1, split[i].length());
//判断尾部是分号,且字符串中没有等号的
if (!(split[i].equals(FileConstant.EQUAL)) && (str.equals(FileConstant.BRANCH))) {
String substring = split[i].substring(0, split[i].length() - 1);
originParam = substring;
cloneParam = (originParam + "_" + randomNum);
log.info("原始数据+克隆数据{}" + originParam + "----------" + cloneParam);
String replace = constant.get(t).replace(originParam, cloneParam);
cloneParamMap.put(replace, replace);
map.put(originParam, cloneParam);
globalList.add(originParam);
break;
}
}
}
}
cloneParamList.add(cloneParamMap);
List<String> list1 = replaceData(map, methodCode);
//去重的符合条件的代码块集合
List<String> list = importStart(list1);
for (String v : list) {
System.out.println("{去重集合数据是}" + v);
}
//取出除方法之外以及大括号之外的代码块
List<Map<String, String>> indoorCodeOutList = getIndoorCodeOut(list);
//返回数据
Map<String, List<List<Map<String, String>>>> maps = new HashMap<>();
//方法内的代码集合
returnList.add(indoorCodeOutList);
//克隆的全局遍历集合
returnList.add(cloneParamList);
//fileName是文件名为了区分
maps.put(fileName, returnList);
return maps;
}
/**
* 获取不符合条件的字符串集合
*
* @param nonConformity 不符合条件的字符串集合
* @param global 全局变量集合
* @return
*/
@Deprecated
public List<String> screen(List<String> nonConformity, List<String> global) {
ArrayList<String> objects = new ArrayList<>();
List<String> list1 = importStart(nonConformity);
for (String s : list1) {
for (int j = 0; j < global.size(); j++) {
if (s.indexOf(global.get(j)) >= 0) {
objects.add(s);
}
}
}
list1.removeAll(objects);
return list1;
}
/**
* 获取方法内的代码块集合
*
* @param getIndoorList
* @return
*/
public static List<Map<String, String>> getIndoorCodeOut(List<String> getIndoorList) {
Map<String, String> map = new HashMap<>();
List<Map<String, String>> list = new ArrayList<>();
for (int i = 0; i < getIndoorList.size(); i++) {
//先对传进来的字符串进行分组
String s = getIndoorList.get(i);
String[] split = s.split("\\s+");
for (int j = 0; j < split.length; j++) {
switch (split[j]) {
case FileConstant.BEFORETHREAD:
String substring1 = s.substring(s.indexOf(FileConstant.BRACKETS_LEFT), s.length() - 1);
map.put(FileConstant.BEFORETHREAD, substring1.substring(1));
break;
case FileConstant.BEFOREPROCESS:
String substring2 = s.substring(s.indexOf(FileConstant.BRACKETS_LEFT), s.length() - 1);
map.put(FileConstant.BEFOREPROCESS, substring2.substring(1));
break;
case FileConstant.BEFORE:
String substring3 = s.substring(s.indexOf(FileConstant.BRACKETS_LEFT), s.length() - 1);
map.put(FileConstant.BEFORE, substring3.substring(1));
break;
case FileConstant.TEST:
String substring4 = s.substring(s.indexOf(FileConstant.BRACKETS_LEFT), s.length() - 1);
String str = substring4.substring(1).trim();
String substring = str.trim().substring(0, str.length());
if (map.get(FileConstant.TEST) != null) {
String o = (String) map.get(FileConstant.TEST);
map.put(FileConstant.TEST, o + "\n" + substring);
} else {
map.put(FileConstant.TEST, "\n" + substring);
}
break;
}
break;
}
}
list.add(map);
return list;
}
/**
* 合并文件
*
* @param list list解释:
* list内得第一个map:key是文件名字,以文件名字来区分是哪个文件得,保证唯一,前面得方法已经判断了是否重复
* list内得第一个map:value则是对应得有几个文件得数据
* List<List<Map<String, String>>>解释:
* 是每个文件得数据,里面同样用到了list,因为有全局变量、代码块、或者其他,且他们自己得数据也有多个
* 里面得map则是记录,例如before注解类里面得代码块,标记代码块是哪个注解里面得
* @return 文件类
*/
public MultipartFile mergeFileCode(List<Map<String, List<List<Map<String, String>>>>> list, List<String> importList) throws Exception {
//创建空的文件
String fileName = createFile();
//全局变量集合
List<String> globalList = new ArrayList<>();
//@BeforeThread集合
List<String> beforeThreadList = new ArrayList<>();
//@Before集合
List<String> beforeList = new ArrayList<>();
//@BeforeProcess集合
List<String> beforeProcessList = new ArrayList<>();
//@Test集合
List<String> testList = new ArrayList<>();
//beforeThreadMap
Map<String, List<String>> beforeThreadMap = new HashMap<>();
//beforeMap
Map<String, List<String>> beforeMap = new HashMap<>();
//beforeProcessMap
Map<String, List<String>> beforeProcessMap = new HashMap<>();
//testMap
Map<String, List<String>> testMap = new HashMap<>();
//循环获取集合中的map,每一个map都是一个file文件,map中的key则是文件名,用文件名区别
for (int l = 0; l < list.size(); l++) {
//这里获取得key是文件名,通过key,也就是文件名遍历
for (String key : list.get(l).keySet()) {
List<List<Map<String, String>>> lists = list.get(l).get(key);
//通过key(文件名)获取该文件对应得list集合数据,其中包括了多个list,对应得是全局变量得list、和代码块得list
for (int i = 0; i < lists.size(); i++) {
List<Map<String, String>> maps = lists.get(i);
for (int j = 0; j < maps.size(); j++) {
Map<String, String> map = maps.get(j);
for (String s : map.keySet()) {
//key和value相等就说明是全局变量,添加到全局变量得集合中
if (s.equals(map.get(s))) {
globalList.add(s);
} else {
//不相等就说明是代码块得数据,进来直接switch
switch (s) {
case FileConstant.BEFORETHREAD:
beforeThreadList.add(map.get(s));
beforeThreadMap.put(s, beforeThreadList);
break;
case FileConstant.BEFORE:
beforeList.add(map.get(s));
beforeMap.put(s, beforeList);
break;
case FileConstant.BEFOREPROCESS:
beforeProcessList.add(map.get(s));
beforeProcessMap.put(s, beforeProcessList);
break;
case FileConstant.TEST:
testList.add(map.get(s));
testMap.put(s, testList);
break;
}
}
}
}
}
}
}
//import数据
for (String a : importStart(importList)) {
System.out.println(a);
}
//全局变量数据
for (String a : importStart(globalList)) {
System.out.println(a);
}
//beforeThread数据
for (String s : beforeThreadMap.keySet()) {
for (String a : importStart(beforeThreadMap.get(s))) {
System.out.println(a);
}
}
//before数据
for (String s : beforeMap.keySet()) {
for (String a : importStart(beforeMap.get(s))) {
System.out.println(a);
}
}
//beforeProcess数据
for (String s : beforeProcessMap.keySet()) {
for (String a : importStart(beforeProcessMap.get(s))) {
System.out.println(a);
}
}
//test数据
for (String s : testMap.keySet()) {
for (String a : importStart(testMap.get(s))) {
System.out.println(a);
}
}
//以上集合数据进行重组,为写入数据最准备
List<String> codeList = getStrings(beforeThreadMap, beforeMap, beforeProcessMap, testMap);
//写入数据
MultipartFile multipartFile = writeFileCode(fileName, globalList, importList, codeList);
return multipartFile;
}
private List<String> getStrings(Map<String, List<String>> beforeThreadMap,
Map<String, List<String>> beforeMap,
Map<String, List<String>> beforeProcessMap,
Map<String, List<String>> testMap) {
List<String> codeList = new ArrayList<>();
String recombination = recombination(beforeThreadMap);
String recombination1 = recombination(beforeMap);
String recombination2 = recombination(beforeProcessMap);
String recombination3 = recombination(testMap);
codeList.add(recombination);
codeList.add(recombination1);
codeList.add(recombination2);
codeList.add(recombination3);
return codeList;
}
/**
* 数据重组,将数据外围包裹上注解、方法、修饰符、符号大括号
*
* @param map
* @return
*/
public String recombination(Map<String, List<String>> map) {
String str = null;
for (String s : map.keySet()) {
switch (s) {
case FileConstant.BEFORETHREAD:
StringBuffer sb1 = new StringBuffer();
sb1.append(FileConstant.BEFORETHREAD);
sb1.append("\n");
sb1.append(FileConstant.startBeforeThread + FileConstant.CURVES + FileConstant.BRACKETS_LEFT);
sb1.append("\n");
for (int i = 0; i < map.get(s).size(); i++) {
String split = split(map.get(s).get(i));
sb1.append(split);
sb1.append("\n");
}
sb1.append(FileConstant.BRACKETS_RIGHT);
str = sb1.toString();
break;
case FileConstant.BEFORE:
StringBuffer sb2 = new StringBuffer();
sb2.append(FileConstant.BEFORE);
sb2.append("\n");
sb2.append(FileConstant.startBefore + FileConstant.CURVES + FileConstant.BRACKETS_LEFT);
sb2.append("\n");
for (int i = 0; i < map.get(s).size(); i++) {
sb2.append(map.get(s).get(i));
sb2.append("\n");
}
sb2.append(FileConstant.BRACKETS_RIGHT);
str = sb2.toString();
break;
case FileConstant.BEFOREPROCESS:
StringBuffer sb3 = new StringBuffer();
sb3.append(FileConstant.BEFOREPROCESS);
sb3.append("\n");
sb3.append(FileConstant.startBeforeProcess + FileConstant.CURVES + FileConstant.BRACKETS_LEFT);
sb3.append("\n");
for (int i = 0; i < map.get(s).size(); i++) {
sb3.append(map.get(s).get(i));
sb3.append("\n");
}
sb3.append(FileConstant.BRACKETS_RIGHT);
str = sb3.toString();
break;
case FileConstant.TEST:
StringBuffer sb4 = new StringBuffer();
sb4.append(FileConstant.TEST);
sb4.append("\n");
sb4.append(FileConstant.startTest + FileConstant.CURVES + FileConstant.BRACKETS_LEFT);
sb4.append("\n");
for (int i = 0; i < map.get(s).size(); i++) {
String s1 = updateGolaData(map.get(s).get(i));
sb4.append(s1);
sb4.append("\n");
}
sb4.append(FileConstant.BRACKETS_RIGHT);
str = sb4.toString();
break;
}
}
return str;
}
/**
* 向文件写入信息
*
* @param fileName
* @param globalList
* @param codeList
* @throws Exception
*/
public MultipartFile writeFileCode(String fileName,
List<String> globalList,
List<String> importList,
List<String> codeList) throws Exception {
//根据传过来的fileName查询文件是否存在,不存在则抛出异常
File file = new File(FileConstant.FILE_DIR_PATH + "/" + fileName);
if (!file.exists()) {
throw new FileException(FileStatusEnum.FILE_NOT_NO.getCode(), FileStatusEnum.FILE_NOT_NO.getMsg());
} else {
//存在则输入数据
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
for (String a : importStart(importList)) {
try {
bw.write(a);
} catch (IOException e) {
e.printStackTrace();
}
}
bw.write("\r\n");
bw.write(FileConstant.RUN_WITH);
bw.write("\r\n");
bw.write(FileConstant.CLAZZ + " " + FileConstant.TEST_RUNER + FileConstant.BRACKETS_LEFT);
bw.write("\r\n");//换行符
for (String b : importStart(globalList)) {
try {
bw.write(b);
bw.write("\r\n");
} catch (IOException e) {
e.printStackTrace();
}
}
for (String c : importStart(codeList)) {
try {
bw.write(c);
bw.write("\r\n");
} catch (IOException e) {
e.printStackTrace();
}
}
bw.write(FileConstant.BRACKETS_RIGHT);
bw.close();
}
FileInputStream fileInputStream = new FileInputStream(file);
MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(),
ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
return multipartFile;
}
/**
* 创建合并脚本
*
* @return
* @throws Exception
*/
public String createFile() throws Exception {
//在服务器上创建文件夹,如果在本地启动,则直接是在tomcat的bin文件下创建了
File dir = new File(FileConstant.FILE_DIR_PATH);
if (!dir.exists()) {
dir.mkdirs();
}
String s = randomNum(5);
File file = new File(FileConstant.FILE_PATH + "_" + s + FileConstant.SUFFIX);
if (!file.exists()) {
file.createNewFile();
log.info("{文件创建成功}");
} else {
throw new FileException(FileStatusEnum.FILE_EXIST.getCode(), FileStatusEnum.FILE_EXIST.getMsg());
}
return file.getName();
}
/**
* 随机数
*
* @return
*/
public String randomNum(int number) {
Random rand = new Random();
char[] letters = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'r',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
String str = "";
int index;
boolean[] flags = new boolean[letters.length];//默认为false
for (int i = 0; i < number; i++) {
do {
index = rand.nextInt(letters.length);
} while (flags[index] == true);
char c = letters[index];
str += c;
flags[index] = true;
}
return str;
}
/**
* 查询字符串中有多少个同样的字符串出现过,返回count
*
* @param str
* @param key
* @return
*/
public int getCount(String str, String key) {
if (str == null || key == null || "".equals(str.trim()) || "".equals(key.trim())) {
return 0;
}
int count = 0;
int index = 0;
while ((index = str.indexOf(key, index)) != -1) {
index = index + key.length();
count++;
}
return count;
}
public List<String> getAnnotation(String str) {
Map<String, String> map = new HashMap<>();
List<String> list = new ArrayList<>();
for (String a : FileConstant.annotationArray) {
if (a.equals(FileConstant.BEFOREPROCESS)) {
String substring1 = str.substring(str.indexOf(a));
String substring = substring1.substring(0, substring1.indexOf(FileConstant.BRACKETS_RIGHT));
list.add((substring + FileConstant.BRACKETS_RIGHT).trim());
String replace = str.replace(a, "");
map.put(FileConstant.BEFORETHREAD, replace);
}
if (a.equals(FileConstant.BEFORETHREAD)) {
String beforeThread = map.get(FileConstant.BEFORETHREAD);
String substring1 = beforeThread.substring(beforeThread.indexOf(a));
String substring = substring1.substring(0, substring1.indexOf(FileConstant.BRACKETS_RIGHT));
list.add((substring + FileConstant.BRACKETS_RIGHT).trim());
String before = beforeThread.replace(a, "");
map.put(FileConstant.BEFORE, before);
}
if (a.equals(FileConstant.BEFORE)) {
String before = map.get(FileConstant.BEFORE);
String before1 = getBefore(before);
//获取@Before到文本最后的位置
String substring = before.substring(before.indexOf(a), before.length() - 1);
String substring2 = substring.substring(0, substring.indexOf(FileConstant.BRACKETS_LEFT));
list.add(substring2 + FileConstant.BRACKETS_LEFT + "\r\n" + (before1 + FileConstant.BRACKETS_RIGHT).trim());
}
}
//获取test注解文本
List<String> testStr = getTestStr(str);
for (String c : testStr) {
list.add(c);
}
return list;
}
/**
* 获取@Test注解的文本
*
* @param str 原始字符串
* @return list 返回list集合
*/
public List<String> getTestStr(String str) {
int i;
String substring1;
int count;
String reverse;
List<String> testList = new ArrayList<>();
//首先获取第一个@Test注解的位置,如何@Test根据indexOf获取的位置是第一个
i = str.indexOf(FileConstant.TEST);
String trim = str.trim();
//从第一个注解位置截取到最后一位,获取到则是@Test注解的全部代码块,可以理解成一个list,或者是单个,不包括整个类开始和结束的大括号
substring1 = trim.substring(i, trim.length() - 1);
//判断test注解有多少个
count = getCount(substring1, FileConstant.TEST);
Map<String, String> strMap = new HashMap<>();
//对字符串进行反转
reverse = revString(substring1);
for (int a = 0; a < count; a++) {
if (a == 0) {
//通过括号和反转test注解获取字符串中,全部的@Test部分,不包括类的大括号
String substring2 = reverse.substring(reverse.indexOf(FileConstant.BRACKETS_RIGHT), reverse.indexOf(FileConstant.REVERSE_TEST));
testList.add(FileConstant.TEST + revString(substring2));
//substring方法,获取剩余的字符串,添加到map中
String s = subString(reverse, reverse.indexOf(FileConstant.REVERSE_TEST), reverse.length());
strMap.put(FileConstant.REVERSE_TEST, s.replaceFirst(FileConstant.REVERSE_TEST, "").trim());
}
if (a > 0) {
String testStr = strMap.get(FileConstant.REVERSE_TEST);
String substring2 = testStr.substring(testStr.indexOf(FileConstant.BRACKETS_RIGHT), testStr.indexOf(FileConstant.REVERSE_TEST));
testList.add(FileConstant.TEST + revString(substring2));
String s = subString(testStr, testStr.indexOf(FileConstant.REVERSE_TEST), testStr.length());
strMap.put(FileConstant.REVERSE_TEST, s.replaceFirst(FileConstant.REVERSE_TEST, "").trim());
}
}
return testList;
}
/**
* 已弃用,循环删除多个下标出现乱码
* 删除指定下标的字符串
*
* @param str 原始字符串
* @param startIndex 开始下标
* @param endIndex 结束下标
* @return
*/
@Deprecated
public String replaceStr(String str, int startIndex, int endIndex) {
Map<String, String> map = new HashMap<>();
char[] chars = str.toCharArray();
for (int i = 0; i <= endIndex; i++) {
if (i == 0) {
String replace = str.replace(chars[i], '~');
String replace1 = replace.replace("~", "");
map.put("str", replace1);
} else {
String str1 = map.get("str");
String replace = str1.replace(chars[i], '~');
String replace1 = replace.replace("~", "");
map.put("str", replace1);
}
}
return str;
}
/**
* 根据下标截取
*
* @param str
* @param startIndex
* @param endIndex
* @return
*/
public String subString(String str, int startIndex, int endIndex) {
String substring = str.substring(startIndex, endIndex);
return substring;
}
/**
* 将字符串反转
*
* @param str
* @return
*/
public String revString(String str) {
StringBuffer sb = new StringBuffer(str);
//将缓冲区的内容反转
sb.reverse();
return sb.toString();
}
/**
* 对全局代码块中的全局变量进行全局修改名称,避免重复
*
* @param map
* @param list
* @return
*/
public List<String> replaceData(Map<String, String> map, List<String> list) {
//返回list
List<String> resultList;
//map集合,key是原始数据,vaue是克隆数据
Set<String> strings = map.keySet();
//随机标识
String s1 = randomNum(5);
//将list中的数据全部拿出来,以唯一标识拼接,拼接成一个字符串,因为循环去改数据有bug
StringBuffer sb = new StringBuffer();
for (String a : list) {
sb.append(a);
sb.append(s1);
}
String replace = null;
List<String> objects = new ArrayList<>();
//将map转为集合
for (String s : strings) {
objects.add(s);
}
//循环去修改字符串中的全局变量,对合成的字符串全体修改
for (int i = 0; i < objects.size(); i++) {
if (i == 0) {
StringBuffer s5 = new StringBuffer(sb);
replace = s5.toString().replace(objects.get(i), map.get(objects.get(i)));
} else {
replace = replace.replace(objects.get(i), map.get(objects.get(i)));
}
}
//上面修改完后根据刚刚生成的随机字符进行分割成数组返回
String[] split = replace.split(s1);
resultList = new ArrayList<>(Arrays.asList(split));
for (String a : resultList) {
System.out.println("对代码块中更换数据:" + a);
}
for (String n : resultList) {
System.out.println("" + n);
}
return resultList;
}
/**
* 根据传过来的多个文件,形成一个文件夹名字
*
* @param str
* @return
*/
public String makeFileName(String str) {
StringBuffer sb = new StringBuffer();
//根据逗号进行分割
String[] split = str.split(FileConstant.COMMA);
for (String s : split) {
String substring = s.substring(0, s.lastIndexOf(FileConstant.SUFFIX));
sb.append(substring);
sb.append(FileConstant.SLASH);
}
return sb.toString().substring(0, sb.length() - 1);
}
/**
* 获取import开头的数据
*
* @param str 原始字符串
* @return
*/
public List<String> getImportStr(String str) {
List<String> list = new ArrayList<>();
String sb = str.substring(0, str.indexOf(FileConstant.RUNWITH));
String[] split = sb.split("\n");
for (String b : split) {
if (StrUtil.startWith(b, FileConstant.IMPORT)) {
list.add(b);
}
}
return list;
}
/**
* 获取开头是全局变量
*
* @param str
* @return
*/
public List<String> getPublicStr(String str) {
List<String> list = new ArrayList<>();
String[] split = str.split("\n");
for (String b : split) {
String trim = b.trim();
if (!StrUtil.endWith(trim, FileConstant.BRACKETS_LEFT) && !trim.contains(FileConstant.CURVES_LEFT) && !trim.contains(FileConstant.CURVES_RIGHT)) {
for (String c : FileConstant.MODIFIER) {
if (StrUtil.startWith(trim, c)) {
list.add(b.trim());
}
//根据数据类型
if (isDataType(trim)) {
list.add(b.trim());
}
}
}
}
return list;
}
/**
* 判断字符串中是否右等号何分号的
*
* @param str
* @return
*/
public boolean isEquaBranch(String str) {
boolean flag = false;
if ((str.indexOf(FileConstant.EQUAL) >= 0) && (str.substring(str.length() - 1).equals(FileConstant.BRANCH))) {
flag = true;
}
return flag;
}
/**
* 根据数据类型判断
*
* @param str
* @return
*/
public boolean isDataType(String str) {
boolean flag = false;
if (str.indexOf(DataTypeConstant.INT_1) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.BYTE_1) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.SHORT_1) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.LONG_1) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.FLOAT_1) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.DOUBLE_1) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.CHAR_1) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.BOOLEAN_1) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.STRING_1) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.DEF_1) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.VAR_1) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.LEF_1) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.INTEGER_2) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.BYTE_2) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.SHORT_2) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.LONG_2) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.FLOAT_2) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.DOUBLE_2) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.CHARACTER_2) >= 0) {
flag = true;
}
if (str.indexOf(DataTypeConstant.BOOLEAN_2) >= 0) {
flag = true;
}
return flag;
}
/**
* 获取before字符串
*
* @param s
* @return
*/
public String getBefore(String s) {
char prefix = '{';
char suffix = '}';
String beforeCode = null;
List<Integer> list = new ArrayList<>();
//传过来的字符串,Before注解肯定是排在最前面的,那么只需要获取其他注解的位置,下标最小的则视为是在Before后面的注解,然后根据此注解作为endSplit
int a = s.indexOf(FileConstant.BEFOREPROCESS);
int b = s.indexOf(FileConstant.TEST);
int c = s.indexOf(FileConstant.BEFORETHREAD);
if (a != -1) {
list.add(a);
}
if (b != -1) {
list.add(b);
}
if (c != -1) {
list.add(c);
}
//对集合进行排序,排第一个的则是最小的
Collections.sort(list);
Map<Integer, String> map = new LinkedHashMap<>();
map.put(a, FileConstant.BEFOREPROCESS);
map.put(b, FileConstant.TEST);
map.put(c, FileConstant.BEFORETHREAD);
//等于0则代表后面没用方法了,也没用方法了。
if (a == -1 && b == -1 && c == -1) {
String substring = s.substring(s.indexOf(prefix), s.length() - 1).trim();
beforeCode = StrUtil.unWrap(substring, prefix, suffix);
} else if (a + b + c >= 0) {
String s1 = map.get(list.get(0));
String substring1 = s.substring(s.indexOf(FileConstant.BEFORE), s.length() - 1);
System.out.println(substring1);
String substring = substring1.substring(substring1.indexOf(prefix), substring1.indexOf(s1)).trim();
beforeCode = StrUtil.unWrap(substring, prefix, suffix);
}
return beforeCode;
}
/**
* 对@BeforeThread注解当中的方法中的record方法,当中的第二个参数为指定的测试方法,现在指定为test
* 对合成的代码当中,全部改为test一样的,因为全局变量修改代码块的时候如果测试方法是一样的,则会进行
* 覆盖掉
*
* @param str
* @return
*/
public String split(String str) {
String substring;
String substring1 = null;
String joint = null;
String[] split = str.split("\n");
for (String s : split) {
if (s.indexOf(FileConstant.RECORD) >= 0) {
//获取左边括号的位置何右边的位置
int i = s.indexOf(FileConstant.CURVES_LEFT);
int i1 = s.indexOf(FileConstant.COMMA);
//获取到形参里面的参数
substring = s.substring(i + 1, i1);
//获取到原始数据
substring1 = s.substring(i, s.length());
//进行拼接
joint = FileConstant.CURVES_LEFT + substring + FileConstant.COMMA + "\"test\")";
}
}
String replace1 = str.replace(substring1, joint);
return replace1;
}
/**
* 主要是获取局部变量的字段
*
* @param str
* @return
*/
public String updateGolaData(String str) {
String replace = null;
//1.对字符串进行分割,以行数分割
String[] split = str.split("\n");
//局部字符串集合
List<String> list = new ArrayList<>();
//局部变量集合
List<String> localList = new ArrayList<>();
//判断,符合条件的则视为局部变量
for (String s : split) {
int i = s.indexOf(FileConstant.EQUAL);
int i1 = s.indexOf(FileConstant.EQUAL_TWO);
if (i >= 0 && i1 <= 0) {
list.add(s);
}
}
for (int i = 0; i < list.size(); i++) {
//对字符串进行分割
String trim = list.get(i).trim();
//以空格分割
String[] split1 = trim.split("\\s+");
if (split1[2].equals(FileConstant.EQUAL)) {
//获取局部变量数据
localList.add(split1[1]);
}
}
//对代码块进行替换数据.替换局部变量数据
for (String localStr : localList) {
int localStr1 = str.indexOf("localStr");
if (localStr1 >= 0) {
throw new FileException(FileStatusEnum.NON_CONFORMITY.getCode(), FileStatusEnum.NON_CONFORMITY.getMsg());
}
//小于零则代表局部变量名是符合规则的,并不是单个字符,或者在代码块中有存在的
if (localStr1 < 0) {
String s1 = randomNum(4);
replace = str.replace(localStr, localStr + "_" + s1);
}
}
//如果没有全局变量,则返回原始数据
if (localList.size() == 0) {
replace = str;
}
return replace;
}
}
常量类:
package org.ngrinder.script.common;
/**
* @Author xiaoxin
* @Date 2021/9/8 9:26
* @Version 1.0
*/
public class DataTypeConstant {
/**
* 数据类型
*/
public static final String INT_1 = "int";
public static final String BYTE_1 = "byte";
public static final String SHORT_1 = "short";
public static final String LONG_1 = "long";
public static final String FLOAT_1 = "float";
public static final String DOUBLE_1 = "double";
public static final String CHAR_1 = "char";
public static final String BOOLEAN_1 = "boolean";
public static final String STRING_1 = "String";
public static final String DEF_1 = "def";
public static final String VAR_1 = "var";
public static final String LEF_1 = "lef";
public static final String INTEGER_2 = "Integer";
public static final String LONG_2 = "Long";
public static final String BOOLEAN_2 = "Boolean";
public static final String SHORT_2 = "Short";
public static final String CHARACTER_2 = "Character";
public static final String BYTE_2 = "Byte";
public static final String FLOAT_2 = "Float";
public static final String DOUBLE_2 = "Double";
}
package org.ngrinder.script.common;
/**
* @Author xiaoxin
* @Date 2021/9/7 17:28
* @Version 1.0
*/
public class FileConstant {
/**
* 类开头注解
*/
public static final String RUNWITH = "@RunWith";
/**
* class
*/
public static final String CLAZZ = "class";
/**
* code
*/
public static final String CODE = "code";
/**
* msg
*/
public static final String MSG = "msg";
/**
* 左小括号
*/
public static final String CURVES_LEFT = "(";
/**
* 右小括号
*/
public static final String CURVES_RIGHT = ")";
/**
* 括号
*/
public static final String CURVES = "()";
/**
* 逗号
*/
public static final String COMMA = ",";
/**
* 合成生成的类名
*/
public static final String TEST_RUNER = "TestRuner";
/**
* 左大括号
*/
public static final String BRACKETS_LEFT = "{";
/**
* 右大括号
*/
public static final String BRACKETS_RIGHT = "}";
/**
*
*/
public static final String SLASH = "/";
/**
* 注解数组
*/
public static final String[] annotationArray = {"@BeforeProcess", "@BeforeThread", "@Before", "@Test"};
/**
* 类之外的import文件引入
*/
public static final String IMPORT = "import";
/**
* @Before注解
*/
public static final String BEFORE = "@Before";
/**
* @test注解
*/
public static final String TEST = "@Test";
public static final String PUBLIC = "public";
/**
* @BeforeProcess注解
*/
public static final String BEFOREPROCESS = "@BeforeProcess";
/**
* @BeforeThread注解
*/
public static final String BEFORETHREAD = "@BeforeThread";
/**
* test注解反转
*/
public static final String REVERSE_TEST = "tseT@";
/**
* 尾部分号
*/
public static final String BRANCH = ";";
/**
* 相等
*/
public static final String EQUAL = "=";
public static final String SPOT = ".";
/**
*
*/
public static final String EQUAL_TWO = "==";
public static final String RUN_WITH = "@RunWith(GrinderRunner)";
/**
* 类上的注解
*/
// public static final String[] RUN_WITH = {"@RunWith(GrinderRunner)", "@RunWith(GrinderRunner.class)"};
/**
* 类开头
*/
public static final String[] START_CLASS = {"public", "class"};
/**
* 修饰符
*/
public static final String[] MODIFIER = {"public", "private", "protected", "default"};
/**
* 方法开头字符串
*/
public static final String startBeforeThread = "public void beforeThread";
public static final String startBefore = "public void before";
public static final String startBeforeProcess = "public static void beforeProcess";
public static final String startTest = "public void test";
/**
* 文件路径
*/
public static final String FILE_DIR_PATH = "/home/ngrinder/scriptFile";
public static final String FILE_DIR_PATHS = "/home/ngrinder/scriptFile/";
/**
* 文件路径加文件首字母
*/
public static final String FILE_PATH = "/home/ngrinder/scriptFile/FuseScript";
/**
* 文件后缀
*/
public static final String SUFFIX = ".groovy";
/**
* 全局record方法
*/
public static final String RECORD = "record";
public static final String DESCRIBE = "合成脚本测试";
public static final String EXCEPTION = "Exception";
public static final String ERROR = "ERROR";
}
package org.ngrinder.script.common;
/**
* @Author xiaoxin
* @Date 2021/9/8 9:42
* @Version 1.0
*/
public enum FileStatusEnum {
UP_TO_TWO(1001,"最少为2个文件才可以进行合并"),
UP_TO_TREE(1002, "目前仅支持最多合成三个文件"),
FILE_EXIST(1003, "文件已存在"),
FILE_NOT_NO(1004, "文件不存在"),
FORBID_MERGE(1005,"已合并的脚本不能再一次进行合并,请选择其他脚本"),
NON_CONFORMITY(1006,"局部变量命名不符合合并规则,请手动进行更改,不得以单个字符、以及变量名和方法内的代码块有任何的重复"),
SCRIPT_FILE_VALID_SUCCESS(1007,"操作完成,验证脚本正常"),
SCRIPT_FILE_VALID_ERROR(1008,"验证失败,当前脚本可能存在语法或其他问题,请查看详细信息");
private int code;
private String msg;
private FileStatusEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
private FileStatusEnum(int code) {
this.code = code;
}
private FileStatusEnum(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}