Skip to content

Commit

Permalink
Merge branch 'main' into Release/1.7.0
Browse files Browse the repository at this point in the history
  • Loading branch information
zhou9584 committed Jan 31, 2023
2 parents 753a241 + aa3c124 commit 5d69269
Show file tree
Hide file tree
Showing 21 changed files with 194 additions and 52 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,15 @@ docker pull ghcr.io/microsoft/hydra-lab-uber:latest

**Step 2. run on your machine with BLOB_CONNECTION_STR**

You may write the content "BLOB_CONNECTION_STR=${YOUR_BLOB_CONNECTION_STR}" in an env file, and pass the path of the file to docker container:
```
docker run [-p 9886:9886] [--name=hydra-lab] --env-file ${YOUR_ENV_FILE_PATH} ghcr.io/microsoft/hydra-lab-uber:latest
You may write the content `BLOB_CONNECTION_STR=${YOUR_BLOB_CONNECTION_STR}` in an env file (e.g. env.txt), and pass the path of the file to docker container:

```bash
docker run --env-file env.txt -p 9886:9886 --name=hydra-lab ghcr.io/microsoft/hydra-lab-uber:latest
```
Or simply run with the env parameter -e:
```
docker run [-p 9886:9886] [--name=hydra-lab] -e BLOB_CONNECTION_STR=${YOUR_BLOB_CONNECTION_STR} ghcr.io/microsoft/hydra-lab-uber:latest

```bash
docker run -e BLOB_CONNECTION_STR=${YOUR_BLOB_CONNECTION_STR} -p 9886:9886 --name=hydra-lab ghcr.io/microsoft/hydra-lab-uber:latest
```

**Step 3. visit front-end page and view your connected devices**
Expand Down
6 changes: 3 additions & 3 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Hydra Lab 的特性包括:
- 支持在不同平台上进行 Appium(Java) 测试:Windows/iOS/Android/浏览器/跨平台。
- 无用例的自动化测试:Monkey test,智能探索测试

更多细节,请参见 [介绍:什么是 Hydra Lab?](https://github.com/microsoft/HydraLab/wiki)
更多细节,请参见 [什么是 Hydra Lab?](https://github.com/microsoft/HydraLab/wiki)

<span id="get-started"></span>
## 入门
Expand Down Expand Up @@ -121,12 +121,12 @@ java -jar agent/build/libs/agent.jar
> Note: If you are a Microsoft FTE and want to onboard to the internal Hydra Lab testing service, please visit [our SharePoint site](https://microsoftapc.sharepoint.com/teams/MMXDocument/SitePages/Hydra-Lab-test-automation-service-onboarding-guideline.aspx) to learn more about the internal service instance.
<span id="for-contributor"></span>
### For Contributor:
### 参与贡献Hydra Lab:

- [Contribute to the Hydra Lab GitHub Project](https://github.com/microsoft/HydraLab/wiki/Contribute-to-the-Hydra-Lab-GitHub-Project)

<span id="who-use-it"></span>
## Who are using Hydra Lab?
## 谁在使用Hydra Lab?

It's already powering the UI test automation of the following Microsoft products:
- Microsoft Phone Link (Windows UWP app) and Link to Windows (Android app)
Expand Down
Binary file added agent/agent_installer/Windows/AgentService.exe
Binary file not shown.
13 changes: 13 additions & 0 deletions agent/agent_installer/Windows/AgentService.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<configuration>
<id>Hydra Lab Agent Service</id>
<name>Hydra Lab Agent Service</name>
<description>Hydra Lab Test Agent Service serves as the test runner and test device manager of the connected Hydra Lab center.</description>

<executable>java</executable>
<arguments>-Xms1024m -Xmx2048m -jar agent.jar</arguments>
<startmode>Automatic</startmode>
<logpath> {LOG_FILE_LOCATION} </logpath>
<log mode="roll-by-time">
<pattern>yyyyMMdd</pattern>
</log>
</configuration>
8 changes: 7 additions & 1 deletion agent/agent_installer/Windows/restartAgent.bat
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@ exit /B
if exist "%temp%\getadmin.vbs" ( del "%temp%\getadmin.vbs" )

echo newfile = %1
set newfile=%1
set newfile = %1
::stop hydra lab agent service
net stop "Hydra Lab Agent Service"
::kill hydra lab agent java process
::Powershell -Command "& {Get-WmiObject Win32_Process -Filter \"name like '%%java%%' and CommandLine like '%%agent%%'\" | Select-Object ProcessId -OutVariable pids;if(-not $pids -eq '' ) {stop-process -id $pids.ProcessId}}"
if "%newfile%"=="" ( echo "No need to update" ) else (
if not exist "%newfile%" ( echo "%newfile% not exist" ) else (
echo "Updating"
del agent.jar
ren "%newfile%" agent.jar
)
)
::start hydra lab agent in command mode
::java -Xms1024m -Xmx4096m -jar .\agent.jar
::start hydra lab agent in windows service mode
net start "Hydra Lab Agent Service"
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.microsoft.hydralab.common.util.ThreadUtils;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.springframework.http.HttpStatus;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
Expand Down Expand Up @@ -94,7 +95,7 @@ private Object[] convertArgs(@NotNull DeviceInfo deviceInfo, @NotNull Logger log
try {
methodArgs[i + 1] = JSONObject.parseObject(actionArgs.get(i), DeviceAction.class);
} catch (Exception e1) {
throw new HydraLabRuntimeException(500, "Convert arg failed!", e1);
throw new HydraLabRuntimeException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Convert arg failed!", e1);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public void runTestOnDevice(TestTask testTask, DeviceInfo deviceInfo, Logger log

private void runByFutureTask(DeviceInfo deviceInfo, TestTask testTask, TestRun testRun) throws Exception {
FutureTask<String> futureTask = new FutureTask<>(() -> {
initTestRunThreadContext(testRun);
run(deviceInfo, testTask, testRun);
return null;
});
Expand All @@ -74,6 +75,14 @@ private void runByFutureTask(DeviceInfo deviceInfo, TestTask testTask, TestRun t
}
}

/**
* TODO Call {@link TestRunThreadContext#init(ITestRun)}
* This method must be called in the test run execution thread.
*/
private void initTestRunThreadContext(TestRun testRun) {

}

private static void saveErrorSummary(TestRun testRun, Exception e) {
String errorStr = e.getClass().getName() + ": " + e.getMessage();
if (errorStr.length() > 255) {
Expand Down Expand Up @@ -201,7 +210,7 @@ protected void reInstallApp(DeviceInfo deviceInfo, TestTask testTask, Logger rep
}

protected void reInstallTestApp(DeviceInfo deviceInfo, TestTask testTask, Logger reportLogger) throws Exception {
if(!shouldInstallTestPackageAsApp()){
if (!shouldInstallTestPackageAsApp()) {
return;
}
if (testTask.getTestAppFile() == null) {
Expand Down
29 changes: 29 additions & 0 deletions azure-pipelines-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,42 @@ stages:
Contents: '*.jar'
TargetFolder: '$(Build.ArtifactStagingDirectory)/center_deploy'
condition: and(succeeded(), eq(variables.fullBuild, 'true'))
- task: Gradle@2
displayName: Package Mac installer
inputs:
gradleWrapperFile: 'gradlew'
tasks: 'packageMacInstaller'
publishJUnitResults: false
javaHomeOption: 'JDKVersion'
jdkVersionOption: '1.11'
sonarQubeRunAnalysis: false
spotBugsAnalysis: false
condition: and(succeeded(), eq(variables.fullBuild, 'true'))
- task: Gradle@2
displayName: Package Windows installer
inputs:
gradleWrapperFile: 'gradlew'
tasks: 'packageWindowsInstaller'
publishJUnitResults: false
javaHomeOption: 'JDKVersion'
jdkVersionOption: '1.11'
sonarQubeRunAnalysis: false
spotBugsAnalysis: false
condition: and(succeeded(), eq(variables.fullBuild, 'true'))
- task: CopyFiles@2
displayName: Copy agent jar
inputs:
SourceFolder: 'agent/build/libs/'
Contents: '*.jar'
TargetFolder: '$(Build.ArtifactStagingDirectory)/agent_deploy'
condition: and(succeeded(), eq(variables.fullBuild, 'true'))
- task: CopyFiles@2
displayName: Copy agent installer
inputs:
SourceFolder: 'build/installer/'
Contents: '*.zip'
TargetFolder: '$(Build.ArtifactStagingDirectory)/agent_deploy'
condition: and(succeeded(), eq(variables.fullBuild, 'true'))
- task: CopyFiles@2
displayName: Copy deploy uber files
inputs:
Expand Down
12 changes: 12 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,15 @@ task jacocoRootReport(type: JacocoReport, group: 'Coverage reports') {
xml.enabled true
}
}

task packageMacInstaller(type: Zip) {
from 'agent/agent_installer/MacOS/iOS'
archiveName 'Hydra_Agent_Installer_Mac.zip'
destinationDir file('build/installer')
}

task packageWindowsInstaller(type: Zip) {
from 'agent/agent_installer/Windows'
archiveName 'Hydra_Agent_Installer_Windows.zip'
destinationDir file('build/installer')
}
22 changes: 19 additions & 3 deletions center/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ version = hydraLabVersion
sourceCompatibility = 11
targetCompatibility = 11

repositories {
mavenCentral()
}

bootJar {
// Specify the out jar file name so that the dockerfile could copy it
// without concerning the name change caused by version change.
Expand Down Expand Up @@ -60,6 +64,18 @@ dependencies {
annotationProcessor 'org.projectlombok:lombok:1.18.20'
}

repositories {
mavenCentral()
}
import org.apache.tools.ant.taskdefs.condition.Os
String npmCommand = 'npm'
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
npmCommand = 'npm.cmd'
}
String reactDir = "${projectDir.parentFile.absolutePath}${File.separator}react"
task installWebFront(type: Exec, group: 'build') {
workingDir reactDir
commandLine npmCommand, 'ci'
}

task buildWebFront(type: Exec, group: 'build', dependsOn: installWebFront) {
workingDir reactDir
commandLine npmCommand, 'run', 'pub'
}
2 changes: 1 addition & 1 deletion center/deploy_startup/alert_notification.html

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,7 @@ public Result<Object> runTestTask(@CurrentSecurityContext SysUser requestor,
if (!userTeamManagementService.checkRequestorTeamRelation(requestor, testTaskSpec.teamId)) {
return Result.error(HttpStatus.UNAUTHORIZED.value(), "Unauthorized, the TestFileSet doesn't belong to user's Teams");
}
if (!testTaskService.checkTestTaskTeamConsistency(testTaskSpec)) {
return Result.error(HttpStatus.UNAUTHORIZED.value(), "Unauthorized, the TestTask is requiring deviceIdentifier that doesn't belong to user's Teams");
}
testTaskService.checkTestTaskTeamConsistency(testTaskSpec);
}
//if the queue is not empty, the task will be added to the queue directly
if (testTaskService.isQueueEmpty() || testTaskService.isDeviceFree(testTaskSpec.deviceIdentifier)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@
import com.microsoft.hydralab.common.entity.common.*;
import com.microsoft.hydralab.common.repository.BlobFileInfoRepository;
import com.microsoft.hydralab.common.repository.StatisticDataRepository;
import com.microsoft.hydralab.common.util.AttachmentService;
import com.microsoft.hydralab.common.util.Const;
import com.microsoft.hydralab.common.util.GlobalConstant;
import com.microsoft.hydralab.common.util.SerializeUtil;
import com.microsoft.hydralab.common.util.*;
import com.microsoft.hydralab.common.util.blob.BlobStorageClient;
import com.microsoft.hydralab.t2c.runner.DriverInfo;
import com.microsoft.hydralab.t2c.runner.T2CJsonParser;
import com.microsoft.hydralab.t2c.runner.TestInfo;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -305,18 +303,18 @@ public void cancelTestTaskById(String taskId, String reason) {

public void checkAccessInfo(String name, String key) {
if (key == null) {
throw new RuntimeException("access key is required!");
throw new HydraLabRuntimeException(HttpStatus.UNAUTHORIZED.value(), "Access key is required!");
}
AccessInfo accessInfo = accessInfoMap.get(name);
if (accessInfo != null) {
throw new RuntimeException("please generate access key first!");
if (accessInfo == null) {
throw new HydraLabRuntimeException(HttpStatus.UNAUTHORIZED.value(), "Please generate access key first!");
}
if (!key.equals(accessInfo.getKey())) {
throw new RuntimeException("error access key!");
throw new HydraLabRuntimeException(HttpStatus.UNAUTHORIZED.value(), "Error access key!");
}
int hour = (int) ((new Date().getTime() - accessInfo.getIngestTime().getTime()) / 1000 / 60 / 60);
if (hour > accessLimit) {
throw new RuntimeException("access key has expired!");
throw new HydraLabRuntimeException(HttpStatus.UNAUTHORIZED.value(), "Access key has expired!");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
import com.microsoft.hydralab.common.entity.common.DeviceInfo;
import com.microsoft.hydralab.common.entity.common.TestTask;
import com.microsoft.hydralab.common.util.Const;
import com.microsoft.hydralab.common.util.HydraLabRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

Expand Down Expand Up @@ -138,46 +140,46 @@ public TestTaskQueuedInfo getTestQueuedInfo(String testTaskId) {
return taskQueuedInfo;
}

public boolean checkTestTaskTeamConsistency(TestTaskSpec testTaskSpec) {
public void checkTestTaskTeamConsistency(TestTaskSpec testTaskSpec) throws HydraLabRuntimeException {
if (TestTask.TestRunningType.APPIUM_CROSS.equals(testTaskSpec.runningType)
|| TestTask.TestRunningType.T2C_JSON_TEST.equals(testTaskSpec.runningType)) {
AgentUser agent = agentManageService.getAgent(testTaskSpec.deviceIdentifier);
if (agent == null) {
return false;
throw new HydraLabRuntimeException(HttpStatus.BAD_REQUEST.value(), "Didn't find AgentUser with given deviceIdentifier!");
}
if (!testTaskSpec.teamId.equals(agent.getTeamId())) {
throw new HydraLabRuntimeException(HttpStatus.BAD_REQUEST.value(), "AgentUser doesn't belong to the given team in spec!");
}
return testTaskSpec.teamId.equals(agent.getTeamId());
} else {
String deviceIdentifier = testTaskSpec.deviceIdentifier;
if (deviceIdentifier.startsWith(Const.DeviceGroup.GROUP_NAME_PREFIX)) {
DeviceGroup deviceGroup = deviceGroupService.getGroupByName(deviceIdentifier);
if (deviceGroup == null) {
return false;
throw new HydraLabRuntimeException(HttpStatus.BAD_REQUEST.value(), "Didn't find DeviceGroup with given deviceIdentifier!");
}
if (testTaskSpec.teamId.equals(deviceGroup.getTeamId())) {
return true;
return;
}
if (!deviceGroup.getIsPrivate()) {
return true;
return;
}
deviceAgentManagementService.checkAccessInfo(deviceIdentifier, testTaskSpec.accessKey);
return true;
} else {
DeviceInfo device = deviceAgentManagementService.getDevice(deviceIdentifier);
if (device == null) {
return false;
throw new HydraLabRuntimeException(HttpStatus.BAD_REQUEST.value(), "Didn't find device with given deviceIdentifier!");
}
AgentUser agent = agentManageService.getAgent(device.getAgentId());
if (agent == null) {
return false;
throw new HydraLabRuntimeException(HttpStatus.BAD_REQUEST.value(), "Didn't find AgentUser with given agent id!");
}
if (testTaskSpec.teamId.equals(agent.getTeamId())) {
return true;
return;
}
if (!device.getIsPrivate()) {
return true;
return;
}
deviceAgentManagementService.checkAccessInfo(deviceIdentifier, testTaskSpec.accessKey);
return true;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.alibaba.fastjson.JSONObject;
import com.microsoft.hydralab.common.util.HydraLabRuntimeException;
import lombok.Data;
import org.springframework.http.HttpStatus;
import org.springframework.util.DigestUtils;

import javax.persistence.Column;
Expand Down Expand Up @@ -55,9 +56,9 @@ public BlobFileInfo(File file, String relativePath, String fileType) {
this.setMd5(DigestUtils.md5DigestAsHex(inputStream));
inputStream.close();
} catch (FileNotFoundException e) {
throw new HydraLabRuntimeException(500, "Generate temp file failed!", e);
throw new HydraLabRuntimeException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Generate temp file failed!", e);
} catch (IOException e) {
throw new HydraLabRuntimeException(500, "Get the MD5 of temp file failed!", e);
throw new HydraLabRuntimeException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Get the MD5 of temp file failed!", e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.microsoft.hydralab.ITestRun;
import com.microsoft.hydralab.agent.runner.ITestRun;
import com.microsoft.hydralab.common.util.Const;
import lombok.Data;
import org.slf4j.Logger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ public File verifyAndSaveFile(@NotNull MultipartFile originFile, String parentDi
String fileSuffix = null;
boolean isMatch = false;
if (filename == null) {
throw new HydraLabRuntimeException(405, "error file type: " + filename);
throw new HydraLabRuntimeException(HttpStatus.BAD_REQUEST.value(), "error file type: " + filename);
}
if (fileTypes != null) {
for (String fileType : fileTypes) {
Expand All @@ -255,7 +255,7 @@ public File verifyAndSaveFile(@NotNull MultipartFile originFile, String parentDi
}
}
if (!isMatch) {
throw new HydraLabRuntimeException(405, "error file type: " + filename);
throw new HydraLabRuntimeException(HttpStatus.BAD_REQUEST.value(), "error file type: " + filename);
}
}

Expand Down
Loading

0 comments on commit 5d69269

Please sign in to comment.