feat(group): add 'madb group clear-all' and 'madb group delete'

Change-Id: Ieb15f0188f05af3ff7735905fc1b6157e68d3d24
diff --git a/doc.go b/doc.go
index d60a6f3..c2dca3e 100644
--- a/doc.go
+++ b/doc.go
@@ -232,6 +232,8 @@
 
 The madb group commands are:
    add         Add members to a device group
+   clear-all   Clear all the existing device groups
+   delete      Delete an existing device group
    remove      Remove members from a device group
    rename      Rename an existing device group
 
@@ -256,6 +258,23 @@
 <member> is a member specifier, which can be one of device serial, qualifier,
 device index (e.g., '@1', '@2'), device nickname, or another device group.
 
+Madb group clear-all - Clear all the existing device groups
+
+Clears all the existing device groups.
+
+Usage:
+   madb group clear-all [flags]
+
+Madb group delete - Delete an existing device group
+
+Deletes an existing device group.
+
+Usage:
+   madb group delete [flags] <group_name1> [<group_name2> ...]
+
+<group_name> the name of an existing device group. You can specify more than one
+group names.
+
 Madb group remove - Remove members from a device group
 
 Removes members from an existing device group. If there are no remaining members
diff --git a/group.go b/group.go
index 2cbaf1d..2874a1b 100644
--- a/group.go
+++ b/group.go
@@ -13,15 +13,15 @@
 )
 
 // TODO(youngseokyoon): implement the following sub-commands.
-//  - delete:    delete a group
 //  - list:      list all the groups and their members
-//  - clear-all: delete all the existing device groups
 
 // TODO(youngseokyoon): use the groups for filtering devices.
 
 var cmdMadbGroup = &cmdline.Command{
 	Children: []*cmdline.Command{
 		cmdMadbGroupAdd,
+		cmdMadbGroupClearAll,
+		cmdMadbGroupDelete,
 		cmdMadbGroupRemove,
 		cmdMadbGroupRename,
 	},
@@ -95,6 +95,65 @@
 	return writeConfig(cfg, filename)
 }
 
+var cmdMadbGroupClearAll = &cmdline.Command{
+	Runner: subCommandRunnerWithFilepath{runMadbGroupClearAll, getDefaultConfigFilePath},
+	Name:   "clear-all",
+	Short:  "Clear all the existing device groups",
+	Long: `
+Clears all the existing device groups.
+`,
+}
+
+func runMadbGroupClearAll(env *cmdline.Env, args []string, filename string) error {
+	cfg, err := readConfig(filename)
+	if err != nil {
+		return err
+	}
+
+	// Reset the groups
+	cfg.Groups = make(map[string][]string)
+
+	return writeConfig(cfg, filename)
+}
+
+var cmdMadbGroupDelete = &cmdline.Command{
+	Runner: subCommandRunnerWithFilepath{runMadbGroupDelete, getDefaultConfigFilePath},
+	Name:   "delete",
+	Short:  "Delete an existing device group",
+	Long: `
+Deletes an existing device group.
+`,
+	ArgsName: "<group_name1> [<group_name2> ...]",
+	ArgsLong: `
+<group_name> the name of an existing device group.
+You can specify more than one group names.
+`,
+}
+
+func runMadbGroupDelete(env *cmdline.Env, args []string, filename string) error {
+	// Check if the arguments are valid.
+	if len(args) < 1 {
+		return env.UsageErrorf("There must be at least one argument.")
+	}
+
+	cfg, err := readConfig(filename)
+	if err != nil {
+		return err
+	}
+	for _, groupName := range args {
+		if !isGroupName(groupName, cfg) {
+			return fmt.Errorf("Not an existing group name: %q", groupName)
+		}
+	}
+
+	// Delete the groups
+	for _, groupName := range args {
+		delete(cfg.Groups, groupName)
+	}
+
+	return writeConfig(cfg, filename)
+}
+
 var cmdMadbGroupRemove = &cmdline.Command{
 	Runner: subCommandRunnerWithFilepath{runMadbGroupRemove, getDefaultConfigFilePath},
 	Name:   "remove",
diff --git a/group_test.go b/group_test.go
index 23c235b..adf3b94 100644
--- a/group_test.go
+++ b/group_test.go
@@ -144,6 +144,128 @@
 	runGroupTests(t, tests)
 }
 
+func TestMadbGroupClearAll(t *testing.T) {
+	tests := []testSequence{
+		{
+			{
+				runMadbGroupClearAll,
+				[]string{},
+				map[string][]string{},
+				false,
+			},
+			{
+				runMadbGroupAdd,
+				[]string{"GROUP1", "SERIAL1", "NICKNAME1"},
+				map[string][]string{
+					"GROUP1": []string{"SERIAL1", "NICKNAME1"},
+				},
+				false,
+			},
+			{
+				runMadbGroupAdd,
+				[]string{"GROUP2", "SERIAL2", "NICKNAME2"},
+				map[string][]string{
+					"GROUP1": []string{"SERIAL1", "NICKNAME1"},
+					"GROUP2": []string{"SERIAL2", "NICKNAME2"},
+				},
+				false,
+			},
+			{
+				runMadbGroupAdd,
+				[]string{"GROUP3", "SERIAL3", "NICKNAME3"},
+				map[string][]string{
+					"GROUP1": []string{"SERIAL1", "NICKNAME1"},
+					"GROUP2": []string{"SERIAL2", "NICKNAME2"},
+					"GROUP3": []string{"SERIAL3", "NICKNAME3"},
+				},
+				false,
+			},
+			{
+				runMadbGroupClearAll,
+				[]string{},
+				map[string][]string{},
+				false,
+			},
+		},
+	}
+
+	runGroupTests(t, tests)
+}
+
+func TestMadbGroupDelete(t *testing.T) {
+	tests := []testSequence{
+		{
+			{
+				runMadbGroupDelete,
+				[]string{},
+				map[string][]string{},
+				true,
+			},
+			{
+				runMadbGroupDelete,
+				[]string{"GROUP1"},
+				map[string][]string{},
+				true,
+			},
+		},
+		{
+			{
+				runMadbGroupAdd,
+				[]string{"GROUP1", "SERIAL1", "NICKNAME1"},
+				map[string][]string{
+					"GROUP1": []string{"SERIAL1", "NICKNAME1"},
+				},
+				false,
+			},
+			{
+				runMadbGroupAdd,
+				[]string{"GROUP2", "SERIAL2", "NICKNAME2"},
+				map[string][]string{
+					"GROUP1": []string{"SERIAL1", "NICKNAME1"},
+					"GROUP2": []string{"SERIAL2", "NICKNAME2"},
+				},
+				false,
+			},
+			{
+				runMadbGroupAdd,
+				[]string{"GROUP3", "SERIAL3", "NICKNAME3"},
+				map[string][]string{
+					"GROUP1": []string{"SERIAL1", "NICKNAME1"},
+					"GROUP2": []string{"SERIAL2", "NICKNAME2"},
+					"GROUP3": []string{"SERIAL3", "NICKNAME3"},
+				},
+				false,
+			},
+			{
+				runMadbGroupDelete,
+				[]string{"GROUP4"},
+				map[string][]string{
+					"GROUP1": []string{"SERIAL1", "NICKNAME1"},
+					"GROUP2": []string{"SERIAL2", "NICKNAME2"},
+					"GROUP3": []string{"SERIAL3", "NICKNAME3"},
+				},
+				true,
+			},
+			{
+				runMadbGroupDelete,
+				[]string{"GROUP3", "GROUP1"},
+				map[string][]string{
+					"GROUP2": []string{"SERIAL2", "NICKNAME2"},
+				},
+				false,
+			},
+			{
+				runMadbGroupDelete,
+				[]string{"GROUP2"},
+				map[string][]string{},
+				false,
+			},
+		},
+	}
+
+	runGroupTests(t, tests)
+}
+
 func TestMadbGroupRemove(t *testing.T) {
 	tests := []testSequence{
 		{