madb: keyword expansion feature for 'madb exec'
This adds the keyword expansion feature to 'madb exec', as described
in issue #3.
Within any arguments of 'madb exec', one can use {{name}}, {{serial}},
{{index}}, which will be replaced by the actual value for the given
device before running the adb command.
Change-Id: Icf24c87e431af7932ecf198e66b529982d6c42aa
diff --git a/doc.go b/doc.go
index 31e2c8f..0253e9b 100644
--- a/doc.go
+++ b/doc.go
@@ -98,10 +98,25 @@
For example, the following line:
- madb -a exec push ./foo.txt /sdcard/foo.txt
+ madb exec push ./foo.txt /sdcard/foo.txt
copies the ./foo.txt file to /sdcard/foo.txt for all the currently connected
-Android devices.
+devices.
+
+There are a few pre-defined keywords that can be expanded within an argument.
+
+ "{{index}}" : the index of the current device, starting from 1.
+ "{{name}}" : the nickname of the current device, or the serial number if a nickname is not set.
+ "{{serial}}" : the serial number of the current device.
+
+For example, the following line:
+
+ madb exec -n=Alice,Bob push ./{{name}}.txt /sdcard/{{name}}.txt
+
+copies the ./Alice.txt file to the device named Alice, and ./Bob.txt to the
+device named Bob. Note that you should type in "{{name}}" as-is, with the
+opening/closing curly braces, similar to when you're using a template library
+such as mustache.
To see the list of available adb commands, type 'adb help'.
diff --git a/exec.go b/exec.go
index 7082644..9cf8c4d 100644
--- a/exec.go
+++ b/exec.go
@@ -18,9 +18,23 @@
For example, the following line:
- madb -a exec push ./foo.txt /sdcard/foo.txt
+ madb exec push ./foo.txt /sdcard/foo.txt
-copies the ./foo.txt file to /sdcard/foo.txt for all the currently connected Android devices.
+copies the ./foo.txt file to /sdcard/foo.txt for all the currently connected devices.
+
+There are a few pre-defined keywords that can be expanded within an argument.
+
+ "{{index}}" : the index of the current device, starting from 1.
+ "{{name}}" : the nickname of the current device, or the serial number if a nickname is not set.
+ "{{serial}}" : the serial number of the current device.
+
+For example, the following line:
+
+ madb exec -n=Alice,Bob push ./{{name}}.txt /sdcard/{{name}}.txt
+
+copies the ./Alice.txt file to the device named Alice, and ./Bob.txt to the device named Bob.
+Note that you should type in "{{name}}" as-is, with the opening/closing curly braces, similar to
+when you're using a template library such as mustache.
To see the list of available adb commands, type 'adb help'.
`,
@@ -36,7 +50,13 @@
sh.ContinueOnError = true
- cmdArgs := append([]string{"-s", d.Serial}, args...)
+ // Expand the keywords before running the command.
+ expandedArgs := make([]string, len(args))
+ for i, arg := range args {
+ expandedArgs[i] = expandKeywords(arg, d)
+ }
+
+ cmdArgs := append([]string{"-s", d.Serial}, expandedArgs...)
cmd := sh.Cmd("adb", cmdArgs...)
return runGoshCommandForDevice(cmd, d, false)
}
diff --git a/madb.go b/madb.go
index ae9648f..1dd3fbd 100644
--- a/madb.go
+++ b/madb.go
@@ -17,6 +17,7 @@
"os/exec"
"path"
"path/filepath"
+ "regexp"
"strconv"
"strings"
"sync"
@@ -672,6 +673,26 @@
return encoder.Encode(data)
}
+// expandKeywords takes a command line argument and a device configuration, and returns a new
+// argument where the predefined keywords ("{{index}}", "{{name}}", "{{serial}}") are expanded.
+func expandKeywords(arg string, d device) string {
+ exp := regexp.MustCompile(`{{(index|name|serial)}}`)
+ result := exp.ReplaceAllStringFunc(arg, func(keyword string) string {
+ switch keyword {
+ case "{{index}}":
+ return strconv.Itoa(d.Index)
+ case "{{name}}":
+ return d.displayName()
+ case "{{serial}}":
+ return d.Serial
+ default:
+ return keyword
+ }
+ })
+
+ return result
+}
+
type pathProvider func() (string, error)
// subCommandRunnerWithFilepath is an adapter that turns the madb name/user subcommand functions into cmdline.Runners.
diff --git a/madb_test.go b/madb_test.go
index 65016b4..769137c 100644
--- a/madb_test.go
+++ b/madb_test.go
@@ -391,3 +391,46 @@
t.Fatalf(`The embedded Gradle script is out of date. Please run "jiri go generate" to regenerate the embedded script.`)
}
}
+
+// TestExpandKeywords tests the "expandKeywords" function, which is used for expanding pre-defined
+// keywords such as "{{serial}}" and "{{name}}".
+func TestExpandKeywords(t *testing.T) {
+ // Sample devices.
+ d1 := device{
+ Serial: "0123456789",
+ Type: realDevice,
+ Qualifiers: nil,
+ Nickname: "Alice",
+ Index: 1,
+ UserID: "10",
+ }
+
+ d2 := device{
+ Serial: "emulator-1234",
+ Type: emulator,
+ Qualifiers: nil,
+ Nickname: "",
+ Index: 2,
+ UserID: "",
+ }
+
+ tests := []struct {
+ arg string
+ d device
+ want string
+ }{
+ {"{{name}}.txt", d1, "Alice.txt"},
+ {"{{serial}}.txt", d1, "0123456789.txt"},
+ {"{{index}}.txt", d1, "1.txt"},
+ {"Hello, {{name}}!", d2, "Hello, emulator-1234!"},
+ {"Hello, {{serial}}!", d2, "Hello, emulator-1234!"},
+ {"{{index}}.txt", d2, "2.txt"},
+ {"{{name}}-{{serial}}.txt", d1, "Alice-0123456789.txt"},
+ }
+
+ for i, test := range tests {
+ if got := expandKeywords(test.arg, test.d); got != test.want {
+ t.Fatalf("unmatched results for tests[%v]: got %v, want %v", i, got, test.want)
+ }
+ }
+}