This commit is contained in:
chenjianxing 2020-05-25 17:26:06 +08:00
commit cf0f35eba3
16 changed files with 75 additions and 65 deletions

View File

@ -13,19 +13,18 @@ import org.apache.jmeter.visualizers.backend.BackendListenerContext;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* JMeter BackendListener扩展, jmx脚本中使用 * JMeter BackendListener扩展, jmx脚本中使用
*/ */
public class APIBackendListenerClient extends AbstractBackendListenerClient implements Serializable { public class APIBackendListenerClient extends AbstractBackendListenerClient implements Serializable {
public final static String TEST_ID = "ms.test.id";
private final static String THREAD_SPLIT = " "; private final static String THREAD_SPLIT = " ";
private final static String ID_SPLIT = "-"; private final static String ID_SPLIT = "-";
private final static String TEST_ID = "id";
private final List<SampleResult> queue = new ArrayList<>(); private final List<SampleResult> queue = new ArrayList<>();
private APITestService apiTestService; private APITestService apiTestService;
@ -33,11 +32,11 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
private APIReportService apiReportService; private APIReportService apiReportService;
// 测试ID // 测试ID
private String id; private String testId;
@Override @Override
public void setupTest(BackendListenerContext context) throws Exception { public void setupTest(BackendListenerContext context) throws Exception {
this.id = context.getParameter(TEST_ID); this.testId = context.getParameter(TEST_ID);
apiTestService = CommonBeanFactory.getBean(APITestService.class); apiTestService = CommonBeanFactory.getBean(APITestService.class);
if (apiTestService == null) { if (apiTestService == null) {
LogUtil.error("apiTestService is required"); LogUtil.error("apiTestService is required");
@ -47,6 +46,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
if (apiReportService == null) { if (apiReportService == null) {
LogUtil.error("apiReportService is required"); LogUtil.error("apiReportService is required");
} }
super.setupTest(context);
} }
@Override @Override
@ -57,7 +57,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
@Override @Override
public void teardownTest(BackendListenerContext context) throws Exception { public void teardownTest(BackendListenerContext context) throws Exception {
TestResult testResult = new TestResult(); TestResult testResult = new TestResult();
testResult.setTestId(id); testResult.setTestId(testId);
testResult.setTotal(queue.size()); testResult.setTotal(queue.size());
// 一个脚本里可能包含多个场景(ThreadGroup)所以要区分开key: 场景Id // 一个脚本里可能包含多个场景(ThreadGroup)所以要区分开key: 场景Id
@ -99,7 +99,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
testResult.getScenarios().addAll(scenarios.values()); testResult.getScenarios().addAll(scenarios.values());
testResult.getScenarios().sort(Comparator.comparing(ScenarioResult::getId)); testResult.getScenarios().sort(Comparator.comparing(ScenarioResult::getId));
apiTestService.changeStatus(id, APITestStatus.Completed); apiTestService.changeStatus(testId, APITestStatus.Completed);
apiReportService.complete(testResult); apiReportService.complete(testResult);
queue.clear(); queue.clear();

View File

@ -3,8 +3,10 @@ package io.metersphere.api.jmeter;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.config.JmeterProperties; import io.metersphere.config.JmeterProperties;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.save.SaveService; import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.util.JMeterUtils; import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.backend.BackendListener;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -19,7 +21,7 @@ public class JMeterService {
@Resource @Resource
private JmeterProperties jmeterProperties; private JmeterProperties jmeterProperties;
public void run(InputStream is) { public void run(String testId, InputStream is) {
String JMETER_HOME = jmeterProperties.getHome(); String JMETER_HOME = jmeterProperties.getHome();
String JMETER_PROPERTIES = JMETER_HOME + "/bin/jmeter.properties"; String JMETER_PROPERTIES = JMETER_HOME + "/bin/jmeter.properties";
JMeterUtils.loadJMeterProperties(JMETER_PROPERTIES); JMeterUtils.loadJMeterProperties(JMETER_PROPERTIES);
@ -27,6 +29,7 @@ public class JMeterService {
try { try {
Object scriptWrapper = SaveService.loadElement(is); Object scriptWrapper = SaveService.loadElement(is);
HashTree testPlan = getHashTree(scriptWrapper); HashTree testPlan = getHashTree(scriptWrapper);
addBackendListener(testId, testPlan);
LocalRunner runner = new LocalRunner(testPlan); LocalRunner runner = new LocalRunner(testPlan);
runner.run(); runner.run();
@ -40,4 +43,14 @@ public class JMeterService {
field.setAccessible(true); field.setAccessible(true);
return (HashTree) field.get(scriptWrapper); return (HashTree) field.get(scriptWrapper);
} }
private void addBackendListener(String testId, HashTree testPlan) {
BackendListener backendListener = new BackendListener();
backendListener.setName(testId);
Arguments arguments = new Arguments();
arguments.addArgument(APIBackendListenerClient.TEST_ID, testId);
backendListener.setArguments(arguments);
backendListener.setClassname(APIBackendListenerClient.class.getCanonicalName());
testPlan.add(testPlan.getArray()[0], backendListener);
}
} }

View File

@ -90,7 +90,7 @@ public class APITestService {
String reportId = apiReportService.create(get(request.getId())); String reportId = apiReportService.create(get(request.getId()));
changeStatus(request.getId(), APITestStatus.Running); changeStatus(request.getId(), APITestStatus.Running);
jMeterService.run(is); jMeterService.run(request.getId(), is);
return reportId; return reportId;
} }

View File

@ -8,6 +8,7 @@ import io.metersphere.base.mapper.ext.ExtLoadTestReportMapper;
import io.metersphere.commons.constants.PerformanceTestStatus; import io.metersphere.commons.constants.PerformanceTestStatus;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.dto.DashboardTestDTO; import io.metersphere.dto.DashboardTestDTO;
import io.metersphere.dto.LoadTestDTO; import io.metersphere.dto.LoadTestDTO;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
@ -130,6 +131,7 @@ public class PerformanceTestService {
} }
final LoadTestWithBLOBs loadTest = new LoadTestWithBLOBs(); final LoadTestWithBLOBs loadTest = new LoadTestWithBLOBs();
loadTest.setUserId(SessionUtils.getUser().getId());
loadTest.setId(UUID.randomUUID().toString()); loadTest.setId(UUID.randomUUID().toString());
loadTest.setName(request.getName()); loadTest.setName(request.getName());
loadTest.setProjectId(request.getProjectId()); loadTest.setProjectId(request.getProjectId());
@ -213,6 +215,7 @@ public class PerformanceTestService {
testReport.setUpdateTime(engine.getStartTime()); testReport.setUpdateTime(engine.getStartTime());
testReport.setTestId(loadTest.getId()); testReport.setTestId(loadTest.getId());
testReport.setName(loadTest.getName()); testReport.setName(loadTest.getName());
testReport.setUserId(SessionUtils.getUser().getId());
// 启动测试 // 启动测试
try { try {

View File

@ -386,7 +386,6 @@ class JMXGenerator {
let testPlan = new TestPlan(test.name); let testPlan = new TestPlan(test.name);
this.addScenarios(testPlan, test.scenarioDefinition); this.addScenarios(testPlan, test.scenarioDefinition);
this.addBackendListener(testPlan, test.id);
this.jmeterTestPlan = new JMeterTestPlan(); this.jmeterTestPlan = new JMeterTestPlan();
this.jmeterTestPlan.put(testPlan); this.jmeterTestPlan.put(testPlan);
@ -424,12 +423,6 @@ class JMXGenerator {
}) })
} }
addBackendListener(testPlan, testId) {
let className = 'io.metersphere.api.jmeter.APIBackendListenerClient';
let args = [new KeyValue("id", testId)];
testPlan.put(new BackendListener(testId, className, args));
}
addScenarioVariables(threadGroup, scenario) { addScenarioVariables(threadGroup, scenario) {
let args = this.replaceKV(scenario.variables); let args = this.replaceKV(scenario.variables);
if (args.length > 0) { if (args.length > 0) {

View File

@ -105,7 +105,7 @@
export default { export default {
name: "MsOrganizationMember", name: "MsOrganizationMember",
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator, MsDialogFooter}, components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator, MsDialogFooter},
created() { activated() {
this.initTableData(); this.initTableData();
}, },
data() { data() {

View File

@ -156,7 +156,7 @@
export default { export default {
name: "MsOrganizationWorkspace", name: "MsOrganizationWorkspace",
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator, MsDialogFooter}, components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator, MsDialogFooter},
mounted() { activated() {
this.list(); this.list();
}, },
computed: { computed: {

View File

@ -148,7 +148,7 @@
} }
}, },
created() { activated() {
this.initTableData(); this.initTableData();
}, },
methods: { methods: {

View File

@ -231,7 +231,7 @@
} }
} }
}, },
created() { activated() {
this.initTableData(); this.initTableData();
}, },
methods: { methods: {

View File

@ -201,7 +201,7 @@
export default { export default {
name: "MsSystemWorkspace", name: "MsSystemWorkspace",
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator, MsDialogFooter}, components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator, MsDialogFooter},
mounted() { activated() {
this.list(); this.list();
}, },
methods: { methods: {

View File

@ -256,7 +256,7 @@
} }
} }
}, },
created() { activated() {
this.initTableData(); this.initTableData();
}, },
methods: { methods: {

View File

@ -68,29 +68,29 @@
<el-input v-model="form.password" autocomplete="off" show-password/> <el-input v-model="form.password" autocomplete="off" show-password/>
</el-form-item> </el-form-item>
<div v-for="(role, index) in form.roles" :key="index"> <div v-for="(role, index) in form.roles" :key="index">
<el-form-item :label="'角色'+index" <el-form-item :label="$t('commons.role')+index"
:prop="'roles.' + index + '.id'" :prop="'roles.' + index + '.id'"
:rules="{required: true, message: '请选择角色', trigger: 'change'}" :rules="{required: true, message: $t('role.please_choose_role'), trigger: 'change'}"
> >
<el-select v-model="role.id" placeholder="选择角色类型"> <el-select v-model="role.id" :placeholder="$t('role.please_choose_role')">
<el-option <el-option
v-for="item in activeRole(role)" v-for="item in activeRole(role)"
:key="item.id" :key="item.id"
:label="item.name" :label="$t('role.' + item.id)"
:value="item.id" :value="item.id"
> >
{{item.name}} {{$t('role.' + item.id)}}
</el-option> </el-option>
</el-select> </el-select>
<el-button @click.prevent="removeRole(role)" style="margin-left: 20px;" v-if="form.roles.length > 1">删除 <el-button @click.prevent="removeRole(role)" style="margin-left: 20px;" v-if="form.roles.length > 1">{{$t('commons.delete')}}
</el-button> </el-button>
</el-form-item> </el-form-item>
<div v-if="role.id === 'org_admin'"> <div v-if="role.id === 'org_admin'">
<el-form-item label="选择组织" <el-form-item :label="$t('organization.select_organization')"
:prop="'roles.' + index + '.ids'" :prop="'roles.' + index + '.ids'"
:rules="{required: true, message: '请选择组织', trigger: 'change'}" :rules="{required: true, message: $t('organization.select_organization'), trigger: 'change'}"
> >
<el-select v-model="role.ids" placeholder="选择组织" multiple> <el-select v-model="role.ids" :placeholder="$t('organization.select_organization')" multiple>
<el-option <el-option
v-for="item in form.orgList" v-for="item in form.orgList"
:key="item.id" :key="item.id"
@ -101,11 +101,11 @@
</el-form-item> </el-form-item>
</div> </div>
<div v-if="role.id === 'test_manager'"> <div v-if="role.id === 'test_manager'">
<el-form-item label="选择工作空间" <el-form-item :label="$t('workspace.select')"
:prop="'roles.' + index + '.ids'" :prop="'roles.' + index + '.ids'"
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}" :rules="{required: true, message: $t('workspace.select'), trigger: 'change'}"
> >
<el-select v-model="role.ids" placeholder="选择工作空间" multiple> <el-select v-model="role.ids" :placeholder="$t('workspace.select')" multiple>
<el-option <el-option
v-for="item in form.wsList" v-for="item in form.wsList"
:key="item.id" :key="item.id"
@ -116,11 +116,11 @@
</el-form-item> </el-form-item>
</div> </div>
<div v-if="role.id ==='test_user'"> <div v-if="role.id ==='test_user'">
<el-form-item label="选择工作空间" <el-form-item :label="$t('workspace.select')"
:prop="'roles.' + index + '.ids'" :prop="'roles.' + index + '.ids'"
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}" :rules="{required: true, message: $t('workspace.select'), trigger: 'change'}"
> >
<el-select v-model="role.ids" placeholder="选择工作空间" multiple> <el-select v-model="role.ids" :placeholder="$t('workspace.select')" multiple>
<el-option <el-option
v-for="item in form.wsList" v-for="item in form.wsList"
:key="item.id" :key="item.id"
@ -131,11 +131,11 @@
</el-form-item> </el-form-item>
</div> </div>
<div v-if="role.id ==='test_viewer'"> <div v-if="role.id ==='test_viewer'">
<el-form-item label="选择工作空间" <el-form-item :label="$t('workspace.select')"
:prop="'roles.' + index + '.ids'" :prop="'roles.' + index + '.ids'"
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}" :rules="{required: true, message: $t('workspace.select'), trigger: 'change'}"
> >
<el-select v-model="role.ids" placeholder="选择工作空间" multiple> <el-select v-model="role.ids" :placeholder="$t('workspace.select')" multiple>
<el-option <el-option
v-for="item in form.wsList" v-for="item in form.wsList"
:key="item.id" :key="item.id"
@ -149,7 +149,7 @@
<el-form-item> <el-form-item>
<template> <template>
<el-button type="success" style="width: 100%;" @click="addRole('createUserForm')" :disabled="btnAddRole">添加角色</el-button> <el-button type="success" style="width: 100%;" @click="addRole('createUserForm')" :disabled="btnAddRole">{{$t('role.add')}}</el-button>
</template> </template>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -177,27 +177,27 @@
<el-input v-model="form.phone" autocomplete="off"/> <el-input v-model="form.phone" autocomplete="off"/>
</el-form-item> </el-form-item>
<div v-for="(role, index) in form.roles" :key="index"> <div v-for="(role, index) in form.roles" :key="index">
<el-form-item :label="'角色'+index" <el-form-item :label="$t('commons.role')+index"
:prop="'roles.' + index + '.id'" :prop="'roles.' + index + '.id'"
:rules="{required: true, message: '请选择角色', trigger: 'change'}" :rules="{required: true, message: $t('role.please_choose_role'), trigger: 'change'}"
> >
<el-select v-model="role.id" placeholder="选择角色类型" :disabled="!!role.id"> <el-select v-model="role.id" :placeholder="$t('role.please_choose_role')" :disabled="!!role.id">
<el-option <el-option
v-for="item in activeRole(role)" v-for="item in activeRole(role)"
:key="item.id" :key="item.id"
:label="item.name" :label="$t('role.' + item.id)"
:value="item.id"> :value="item.id">
</el-option> </el-option>
</el-select> </el-select>
<el-button @click.prevent="removeRole(role)" style="margin-left: 20px;" v-if="form.roles.length > 1">删除 <el-button @click.prevent="removeRole(role)" style="margin-left: 20px;" v-if="form.roles.length > 1">{{$t('commons.delete')}}
</el-button> </el-button>
</el-form-item> </el-form-item>
<div v-if="role.id === 'org_admin'"> <div v-if="role.id === 'org_admin'">
<el-form-item label="选择组织" <el-form-item :label="$t('organization.select_organization')"
:prop="'roles.' + index + '.ids'" :prop="'roles.' + index + '.ids'"
:rules="{required: true, message: '请选择组织', trigger: 'change'}" :rules="{required: true, message: $t('organization.select_organization'), trigger: 'change'}"
> >
<el-select v-model="role.ids" placeholder="选择组织" multiple> <el-select v-model="role.ids" :placeholder="$t('organization.select_organization')" multiple>
<el-option <el-option
v-for="item in form.orgList" v-for="item in form.orgList"
:key="item.id" :key="item.id"
@ -208,11 +208,11 @@
</el-form-item> </el-form-item>
</div> </div>
<div v-if="role.id === 'test_manager'"> <div v-if="role.id === 'test_manager'">
<el-form-item label="选择工作空间" <el-form-item :label="$t('workspace.select')"
:prop="'roles.' + index + '.ids'" :prop="'roles.' + index + '.ids'"
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}" :rules="{required: true, message: $t('workspace.select'), trigger: 'change'}"
> >
<el-select v-model="role.ids" placeholder="选择工作空间" multiple> <el-select v-model="role.ids" :placeholder="$t('workspace.select')" multiple>
<el-option <el-option
v-for="item in form.wsList" v-for="item in form.wsList"
:key="item.id" :key="item.id"
@ -223,11 +223,11 @@
</el-form-item> </el-form-item>
</div> </div>
<div v-if="role.id ==='test_user'"> <div v-if="role.id ==='test_user'">
<el-form-item label="选择工作空间" <el-form-item :label="$t('workspace.select')"
:prop="'roles.' + index + '.ids'" :prop="'roles.' + index + '.ids'"
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}" :rules="{required: true, message: $t('workspace.select'), trigger: 'change'}"
> >
<el-select v-model="role.ids" placeholder="选择工作空间" multiple> <el-select v-model="role.ids" :placeholder="$t('workspace.select')" multiple>
<el-option <el-option
v-for="item in form.wsList" v-for="item in form.wsList"
:key="item.id" :key="item.id"
@ -238,11 +238,11 @@
</el-form-item> </el-form-item>
</div> </div>
<div v-if="role.id ==='test_viewer'"> <div v-if="role.id ==='test_viewer'">
<el-form-item label="选择工作空间" <el-form-item :label="$t('workspace.select')"
:prop="'roles.' + index + '.ids'" :prop="'roles.' + index + '.ids'"
:rules="{required: true, message: '请选择工作空间', trigger: 'change'}" :rules="{required: true, message: $t('workspace.select'), trigger: 'change'}"
> >
<el-select v-model="role.ids" placeholder="选择工作空间" multiple> <el-select v-model="role.ids" :placeholder="$t('workspace.select')" multiple>
<el-option <el-option
v-for="item in form.wsList" v-for="item in form.wsList"
:key="item.id" :key="item.id"
@ -255,7 +255,7 @@
</div> </div>
<el-form-item> <el-form-item>
<template> <template>
<el-button type="success" style="width: 100%;" @click="addRole('updateUserForm')" :disabled="btnAddRole">添加角色</el-button> <el-button type="success" style="width: 100%;" @click="addRole('updateUserForm')" :disabled="btnAddRole">{{$t('role.add')}}</el-button>
</template> </template>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -386,7 +386,7 @@
} }
} }
}, },
created() { activated() {
this.search(); this.search();
this.getAllRole(); this.getAllRole();
}, },

View File

@ -130,7 +130,7 @@
total: 0, total: 0,
} }
}, },
created: function () { activated: function () {
this.initTableData(); this.initTableData();
}, },
methods: { methods: {

View File

@ -56,7 +56,6 @@ export default {
'personal_information': 'Personal Information', 'personal_information': 'Personal Information',
'exit_system': 'Exit System', 'exit_system': 'Exit System',
'verification': 'Verification', 'verification': 'Verification',
'set_admin': 'Set Admin',
'system_parameter_setting': 'System Parameter Setting', 'system_parameter_setting': 'System Parameter Setting',
'connection_successful': 'Connection successful', 'connection_successful': 'Connection successful',
'connection_failed': 'Connection failed', 'connection_failed': 'Connection failed',
@ -164,6 +163,7 @@ export default {
'test_manager': 'Test Manager', 'test_manager': 'Test Manager',
'test_user': 'Test User', 'test_user': 'Test User',
'test_viewer': 'Test Viewer', 'test_viewer': 'Test Viewer',
'add': 'Add Role',
}, },
report: { report: {
'recent': 'Recent Report', 'recent': 'Recent Report',

View File

@ -80,7 +80,6 @@ export default {
'weeks_5': '周五', 'weeks_5': '周五',
'weeks_6': '周六', 'weeks_6': '周六',
'test_unit': '测试', 'test_unit': '测试',
'set_admin': '设置为管理员',
'system_parameter_setting': '系统参数设置', 'system_parameter_setting': '系统参数设置',
'connection_successful': '连接成功', 'connection_successful': '连接成功',
'connection_failed': '连接失败', 'connection_failed': '连接失败',
@ -161,7 +160,8 @@ export default {
'org_admin': '组织管理员', 'org_admin': '组织管理员',
'test_manager': '测试经理', 'test_manager': '测试经理',
'test_user': '测试人员', 'test_user': '测试人员',
'test_viewer': 'Viewer' 'test_viewer': 'Viewer',
'add': '添加角色',
}, },
report: { report: {
'recent': '最近的报告', 'recent': '最近的报告',

View File

@ -153,7 +153,8 @@ export default {
'org_admin': '組織管理員', 'org_admin': '組織管理員',
'test_manager': '測試經理', 'test_manager': '測試經理',
'test_user': '測試人員', 'test_user': '測試人員',
'test_viewer': 'Viewer' 'test_viewer': 'Viewer',
'add': '添加角色',
}, },
report: { report: {
'name': '項目名稱', 'name': '項目名稱',