blob: 98e955621c1f723a2046a5289903b7a2551efbec [file] [log] [blame]
/*
* Copyright 2015 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cherry
import (
"log"
"../rtdb"
"strings"
"time"
"fmt"
)
type Void struct {}
// RPCHandler
type RPCHandler struct {
rtdbServer *rtdb.Server
testRunner *TestRunner
}
func NewRPCHandler (rtdbServer *rtdb.Server, testRunner *TestRunner) *RPCHandler {
return &RPCHandler {
rtdbServer: rtdbServer,
testRunner: testRunner,
}
}
// RPCClient
type RPCClient struct {
listener *rtdb.Listener
}
func NewRPCClient (listener *rtdb.Listener) *RPCClient {
return &RPCClient {
listener: listener,
}
}
// Subscribe
type SubscribeArgs struct {
Objects []rtdb.TypedObject `json:"objects"`
}
func (handler *RPCHandler) Subscribe (client *RPCClient, args SubscribeArgs) (bool, error) {
log.Printf("[rpc] subscribe %d objects\n", len(args.Objects))
// Subscribe to object.
err := client.listener.Subscribe(args.Objects)
if err != nil { return false, err }
return true, nil
}
// Unsubsribe
type UnsubscribeArgs struct {
Objects []rtdb.TypedObject `json:"objects"`
}
func (handler *RPCHandler) Unsubscribe (client *RPCClient, args UnsubscribeArgs) (bool, error) {
log.Printf("[rpc] unsubscribe %d objects\n", len(args.Objects))
err := client.listener.Unsubscribe(args.Objects)
return err == nil, err
}
// ExecuteTestBatch
type DeviceConfigArgs struct {
TargetAddress string `json:"targetAddress"`
TargetPort int `json:"targetPort"`
SpawnLocalProcess string `json:"spawnLocalProcess"`
DeviceId string `json:"deviceId"`
}
type TestPackageConfigArgs struct {
TestBinaryName string `json:"testBinaryName"`
TestBinaryCommandLine string `json:"testBinaryCommandLine"`
TestBinaryWorkingDir string `json:"testBinaryWorkingDir"`
}
// \todo [2017-03-15 kraita] Embedded structs could be replaced by object references?
type ExecuteTestBatchArgs struct {
DeviceConfig DeviceConfigArgs `json:"deviceConfig"`
TestPackageConfig TestPackageConfigArgs `json:"testPackageConfig"`
TestNameFilters string `json:"testNameFilters"`
}
func getDefaultBatchName () string {
return time.Now().Format(defaultHumanReadableTimeFormat)
}
func (handler *RPCHandler) ExecuteTestBatch (client *RPCClient, args ExecuteTestBatchArgs) (string, error) {
log.Println("[rpc] ExecuteTestBatch requested")
// Check that the specified device exists.
err := handler.rtdbServer.GetObject(args.DeviceConfig.DeviceId, &DeviceConfig{})
if err != nil { return "", err }
// Generate time-based id for batch result.
startTime := time.Now()
// Execute tests in background.
execParams := BatchExecParams {
TargetAddress: args.DeviceConfig.TargetAddress,
TargetPort: args.DeviceConfig.TargetPort,
SpawnLocalProcess: args.DeviceConfig.SpawnLocalProcess,
DeviceId: args.DeviceConfig.DeviceId,
TestBinaryName: args.TestPackageConfig.TestBinaryName,
TestBinaryCommandLine: args.TestPackageConfig.TestBinaryCommandLine,
TestBinaryWorkingDir: args.TestPackageConfig.TestBinaryWorkingDir,
TestNameFilters: strings.Split(args.TestNameFilters, ";"),
}
batchResultId, err := handler.testRunner.ExecuteTestBatch(getDefaultBatchName(), execParams, startTime)
if err != nil { return "", err }
return batchResultId, nil
}
// ExecuteSubTestBatch
// Execute a test batch with parameters identical to another batch, except that the case list is filtered further.
type ExecuteSubTestBatchArgs struct {
OriginalBatchResultId string `json:"originalBatchResultId"`
TestNameFilters string `json:"testNameFilters"`
}
func (handler *RPCHandler) ExecuteSubTestBatch (client *RPCClient, args ExecuteSubTestBatchArgs) (string, error) {
log.Println("[rpc] ExecuteSubTestBatch requested")
var original BatchResult
err := handler.rtdbServer.GetObject(args.OriginalBatchResultId, &original)
if err != nil { return "", err }
var originalCaseList TestCaseList
err = handler.rtdbServer.GetObject(args.OriginalBatchResultId, &originalCaseList)
if err != nil { return "", err }
startTime := time.Now()
testCasePaths := filterTestCaseNames(originalCaseList.Paths, strings.Split(args.TestNameFilters, ";"))
batchResultId, err := handler.testRunner.ExecuteTestBatchWithCaseList(getDefaultBatchName(), original.ExecParams, startTime, testCasePaths)
if err != nil { return "", err }
return batchResultId, nil
}
type ExecuteTestSetArgs struct {
DeviceConfig DeviceConfigArgs `json:"deviceConfig"`
TestPackageConfig TestPackageConfigArgs `json:"testPackageConfig"`
TestSetId string `json:"testSetId"`
}
func (handler *RPCHandler) ExecuteTestSet (client *RPCClient, args ExecuteTestSetArgs) (string, error) {
log.Println("[rpc] ExecuteTestSet requested")
// Check that the specified device exists.
err := handler.rtdbServer.GetObject(args.DeviceConfig.DeviceId, &DeviceConfig{})
if err != nil { return "", err }
startTime := time.Now()
// Execute tests in background.
execParams := BatchExecParams {
TargetAddress: args.DeviceConfig.TargetAddress,
TargetPort: args.DeviceConfig.TargetPort,
SpawnLocalProcess: args.DeviceConfig.SpawnLocalProcess,
DeviceId: args.DeviceConfig.DeviceId,
TestBinaryName: args.TestPackageConfig.TestBinaryName,
TestBinaryCommandLine: args.TestPackageConfig.TestBinaryCommandLine,
TestBinaryWorkingDir: args.TestPackageConfig.TestBinaryWorkingDir,
TestNameFilters: make([]string, 0),
}
// Test sets can have some tens of MBs data. Should not load the
// full test set in the server thread. Passing the test set ID to
// the runner instead.
log.Printf("Requesting execution of test set: %s", args.TestSetId)
batchResultId, err := handler.testRunner.ExecuteTestBatchWithTestSet(getDefaultBatchName(), execParams, startTime, args.TestSetId)
if err != nil { return "", err }
return batchResultId, nil
}
// WouldQueueWithOnlyDifferentDevices
type WouldQueueWithOnlyDifferentDevicesArgs struct {
TargetAddress string `json:"targetAddress"`
TargetPort int `json:"targetPort"`
DeviceId string `json:"deviceId"`
}
// Tells whether the queue that the execution with the given address and port would go to
// currently contains only devices other than args.DeviceId .
func (handler *RPCHandler) WouldQueueWithOnlyDifferentDevices (client *RPCClient, args WouldQueueWithOnlyDifferentDevicesArgs) (bool, error) {
log.Printf("[rpc] WouldQueueWithDifferentDevice called: %v\n", args)
queueId := AddressQueueId(args.TargetAddress, args.TargetPort)
var queue DeviceBatchQueue
err := handler.rtdbServer.GetObject(queueId, &queue)
if err != nil { return false, nil }
differentDeviceFound := false
for _, batchResultId := range queue.BatchResultIds {
var batchResult BatchResult
err := handler.rtdbServer.GetObject(batchResultId, &batchResult)
if err == nil {
if batchResult.ExecParams.DeviceId == args.DeviceId {
return false, nil
} else {
differentDeviceFound = true
}
}
}
return differentDeviceFound, nil
}
// DeleteDeviceConfig
func (handler *RPCHandler) DeleteDeviceConfig (client *RPCClient, deviceId string) (bool, error) {
opSet := rtdb.NewOpSet()
opSet.Delete(typeDeviceConfig, deviceId)
opSet.Call(typeDeviceConfigList, "deviceConfigList", "Remove", deviceId)
err := handler.rtdbServer.ExecuteOpSet(opSet)
return err == nil, err
}
// SaveDeviceConfig
type SaveDeviceConfigArgs struct {
Id string `json:"id"`
Config DeviceConfig `json:"config"`
}
func (handler *RPCHandler) SaveDeviceConfig (client *RPCClient, args *SaveDeviceConfigArgs) (bool, error) {
log.Printf("SaveDeviceConfig: %s, %#v\n", args.Id, args.Config)
opSet := rtdb.NewOpSet()
// If no id specified, create a new one.
var deviceId string
if args.Id == "" {
deviceId = genRandomId(8)
} else {
deviceId = args.Id
}
if !args.Config.IsADBDevice {
// Append to list or rename old one.
header := DeviceConfigHeader{ deviceId }
opSet.Call(typeDeviceConfigList, "deviceConfigList", "Append", header)
}
// Save device config.
opSet.Call(typeDeviceConfig, deviceId, "Init", args.Config)
// Store results.
err := handler.rtdbServer.ExecuteOpSet(opSet)
if err != nil {
return false, err
}
return true, nil
}
// SetBatchResultName
type SetBatchResultNameArgs struct {
BatchResultId string `json:"batchResultId"`
Name string `json:"name"`
}
func (handler *RPCHandler) SetBatchResultName (client *RPCClient, args *SetBatchResultNameArgs) (bool, error) {
log.Printf("[rpc] SetBatchResultName(%s, %s)\n", args.BatchResultId, args.Name)
opSet := rtdb.NewOpSet()
opSet.Call(typeBatchResultList, "batchResultList", "SetBatchResultName", args.BatchResultId, args.Name)
opSet.Call(typeBatchResult, args.BatchResultId, "SetName", args.Name)
err := handler.rtdbServer.ExecuteOpSet(opSet)
return err == nil, err
}
// StopBatchExecution
type StopBatchExecutionArgs struct {
BatchResultId string `json:"batchResultId"`
}
func (handler *RPCHandler) StopBatchExecution (client *RPCClient, args *StopBatchExecutionArgs) (bool, error) {
log.Printf("[rpc] StopBatchExecution(%s)\n", args.BatchResultId)
err := handler.testRunner.StopBatchExecution(args.BatchResultId)
if err != nil { return false, err }
return true, nil
}
// ContinueBatchExecution
type ContinueBatchExecutionArgs struct {
BatchResultId string `json:"batchResultId"`
}
func (handler *RPCHandler) ContinueBatchExecution (client *RPCClient, args *ContinueBatchExecutionArgs) (bool, error) {
log.Printf("[rpc] ContinueBatchExecution(%s)\n", args.BatchResultId)
err := handler.testRunner.ContinueBatchExecution(args.BatchResultId)
if err != nil { return false, err }
return true, nil
}
// MoveBatchInQueue
type MoveBatchInQueueArgs struct {
BatchResultId string `json:"batchResultId"`
Offset int `json:"offset"`
}
func (handler *RPCHandler) MoveBatchInQueue (client *RPCClient, args *MoveBatchInQueueArgs) (bool, error) {
log.Printf("[rpc] MoveBatchInQueue(%s, %d)\n", args.BatchResultId, args.Offset)
err := handler.testRunner.MoveBatchInQueue(args.BatchResultId, args.Offset)
if err != nil { return false, err }
return true, nil
}
// NewVersionView
func (handler *RPCHandler) NewVersionView (client *RPCClient, args struct{}) (int, error) {
viewId := client.listener.NewVersionView()
return viewId, nil
}
// ReleaseVersionView
type ReleaseVersionViewArgs struct {
ViewId int `json:"viewId"`
}
func (handler *RPCHandler) ReleaseVersionView (client *RPCClient, args ReleaseVersionViewArgs) (struct{}, error) {
return struct{}{}, client.listener.ReleaseVersionView(args.ViewId)
}
// GetVersionViewedObjects
type GetVersionViewedObjectsArgs struct {
ViewId int `json:"viewId"`
Objects []rtdb.TypedObject `json:"objects"`
}
func (handler *RPCHandler) GetVersionViewedObjects (client *RPCClient, args GetVersionViewedObjectsArgs) ([]rtdb.ObjectBase, error) {
log.Printf("[rpc] GetVersionViewedObjects(%d, [%d objects])\n", args.ViewId, len(args.Objects))
result := []rtdb.ObjectBase{}
for _, objInfo := range args.Objects {
object, err := client.listener.GetVersionViewedObject(objInfo, args.ViewId)
if err != nil { return nil, fmt.Errorf("At %s object '%s': %v", objInfo.Type, objInfo.ObjId, err) }
result = append(result, object)
}
return result, nil
}
// GetTestCaseList
func (handler *RPCHandler) GetTestCaseList (client *RPCClient, args struct{}) ([]string, error) {
return handler.testRunner.fullTestCaseList, nil
}
// Delete TestSet
func (handler *RPCHandler) DeleteTestSet (client *RPCClient, testSetId string) (bool, error) {
opSet := rtdb.NewOpSet()
opSet.Delete(typeTestSet, testSetId)
opSet.Call(typeTestSetList, "testSetList", "Remove", testSetId)
err := handler.rtdbServer.ExecuteOpSet(opSet)
return err == nil, err
}