tast-test: Introduce session.RetrieveActiveSessions.

This is ported from login_RetrieveActiveSessions.

BUG=chromium:899758
TEST=Ran locally.

Change-Id: I63fd066354de9b2d6301a665a09365f3d4f32d79
Reviewed-on: https://chromium-review.googlesource.com/1384326
Commit-Ready: Hidehiko Abe <hidehiko@chromium.org>
Tested-by: Hidehiko Abe <hidehiko@chromium.org>
Reviewed-by: Dan Erat <derat@chromium.org>
diff --git a/src/chromiumos/tast/local/bundles/cros/session/retrieve_active_sessions.go b/src/chromiumos/tast/local/bundles/cros/session/retrieve_active_sessions.go
new file mode 100644
index 0000000..aa82f00
--- /dev/null
+++ b/src/chromiumos/tast/local/bundles/cros/session/retrieve_active_sessions.go
@@ -0,0 +1,91 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package session
+
+import (
+	"context"
+
+	"chromiumos/tast/local/cryptohome"
+	"chromiumos/tast/local/session"
+	"chromiumos/tast/local/upstart"
+	"chromiumos/tast/testing"
+)
+
+func init() {
+	testing.AddTest(&testing.Test{
+		Func:         RetrieveActiveSessions,
+		Desc:         "Ensures that the session_manager correctly tracks active sessions",
+		Attr:         []string{"informational"},
+		SoftwareDeps: []string{"chrome_login"},
+	})
+}
+
+func RetrieveActiveSessions(ctx context.Context, s *testing.State) {
+	if err := upstart.RestartJob(ctx, "ui"); err != nil {
+		s.Fatal("Failed to restart session_manager: ", err)
+	}
+
+	const (
+		user1 = "first_user@nowhere.com"
+		user2 = "second_user@nowhere.com"
+	)
+
+	verify := func(as map[string]string, users ...string) {
+		if len(as) != len(users) {
+			s.Fatalf("Unexpected active sessions: got %v; want %v", as, users)
+		}
+		for _, u := range users {
+			if _, ok := as[u]; !ok {
+				s.Fatalf("Unexpected active sessions: got %v; want %v", as, users)
+			}
+		}
+	}
+
+	// Create clean vault.
+	if err := cryptohome.RemoveVault(ctx, user1); err != nil {
+		s.Fatalf("Failed to clear user dir for %s: %v", user1, err)
+	}
+	if err := cryptohome.RemoveVault(ctx, user2); err != nil {
+		s.Fatalf("Failed to clear user dir for %s: %v", user2, err)
+	}
+	if err := cryptohome.CreateVault(ctx, user1, ""); err != nil {
+		s.Fatalf("Failed to create user dir for %s: %v", user1, err)
+	}
+	defer cryptohome.RemoveVault(ctx, user1)
+	if err := cryptohome.CreateVault(ctx, user2, ""); err != nil {
+		s.Fatalf("Failed to create user dir for %s: %v", user2, err)
+	}
+	defer cryptohome.RemoveVault(ctx, user2)
+	// Before removing vaults, let users log out by restarting
+	// session_manager.
+	defer upstart.RestartJob(ctx, "ui")
+
+	sm, err := session.NewSessionManager(ctx)
+	if err != nil {
+		s.Fatal("Failed to create session_manager binding: ", err)
+	}
+
+	// Start first session.
+	if err = sm.StartSession(ctx, user1, ""); err != nil {
+		s.Fatalf("Failed to start new session for %s: %v", user1, err)
+	}
+
+	if as, err := sm.RetrieveActiveSessions(ctx); err != nil {
+		s.Fatal("Failed to retrieve active sessions: ", err)
+	} else {
+		verify(as, user1)
+	}
+
+	// Add second session.
+	if err = sm.StartSession(ctx, user2, ""); err != nil {
+		s.Fatalf("Failed to start new session for %s: %v", user2, err)
+	}
+
+	if as, err := sm.RetrieveActiveSessions(ctx); err != nil {
+		s.Fatal("Failed to retrieve active sessions: ", err)
+	} else {
+		verify(as, user1, user2)
+	}
+}
diff --git a/src/chromiumos/tast/local/cryptohome/cryptohome.go b/src/chromiumos/tast/local/cryptohome/cryptohome.go
index 3b5d2e2..2ec3bc4 100644
--- a/src/chromiumos/tast/local/cryptohome/cryptohome.go
+++ b/src/chromiumos/tast/local/cryptohome/cryptohome.go
@@ -236,7 +236,7 @@
 			return err
 		}
 		return nil
-	}, &testing.PollOptions{Timeout: 6 * time.Second, Interval: 1 * time.Second})
+	}, &testing.PollOptions{Timeout: 30 * time.Second, Interval: 1 * time.Second})
 
 	if err != nil {
 		return errors.Wrapf(err, "failed to create vault for %s", user)
diff --git a/src/chromiumos/tast/local/session/session_manager.go b/src/chromiumos/tast/local/session/session_manager.go
index d419641..b1da5bd 100644
--- a/src/chromiumos/tast/local/session/session_manager.go
+++ b/src/chromiumos/tast/local/session/session_manager.go
@@ -118,6 +118,16 @@
 	return ret, nil
 }
 
+// RetrieveActiveSessions calls SessionManager.RetrieveActiveSessions D-Bus method.
+func (m *SessionManager) RetrieveActiveSessions(ctx context.Context) (map[string]string, error) {
+	c := m.call(ctx, "RetrieveActiveSessions")
+	var ret map[string]string
+	if err := c.Store(&ret); err != nil {
+		return nil, err
+	}
+	return ret, nil
+}
+
 // call is thin wrapper of CallWithContext for convenience.
 func (m *SessionManager) call(ctx context.Context, method string, args ...interface{}) *dbus.Call {
 	return m.obj.CallWithContext(ctx, dbusInterface+"."+method, 0, args...)