text:
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/arc/intent_helper/arc_external_protocol_dialog.h"

#include "ash/components/arc/arc_prefs.h"
#include "ash/components/arc/session/arc_bridge_service.h"
#include "ash/components/arc/session/arc_service_manager.h"
#include "ash/components/arc/test/connection_holder_util.h"
#include "base/time/time.h"
#include "chrome/browser/ash/arc/arc_web_contents_data.h"
#include "chrome/browser/sharing/click_to_call/click_to_call_ui_controller.h"
#include "chrome/browser/sharing/fake_device_info.h"
#include "chrome/browser/sharing/mock_sharing_service.h"
#include "chrome/browser/sharing/proto/click_to_call_message.pb.h"
#include "chrome/browser/sharing/proto/sharing_message.pb.h"
#include "chrome/browser/sharing/sharing_service_factory.h"
#include "chrome/browser/ui/app_list/arc/arc_app_test.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chromeos/dbus/session_manager/fake_session_manager_client.h"
#include "components/arc/intent_helper/arc_intent_helper_bridge.h"
#include "components/arc/test/fake_intent_helper_instance.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "url/gurl.h"

using ::testing::Property;

namespace arc {

namespace {

// Helper class to run tests that need a dummy WebContents and arc bridge.
class ArcExternalProtocolDialogTestUtils : public BrowserWithTestWindowTest {
 public:
  ArcExternalProtocolDialogTestUtils() = default;
  ArcExternalProtocolDialogTestUtils(
      const ArcExternalProtocolDialogTestUtils&) = delete;
  ArcExternalProtocolDialogTestUtils& operator=(
      const ArcExternalProtocolDialogTestUtils&) = delete;

  void SetUp() override {
    chromeos::SessionManagerClient::InitializeFakeInMemory();
    chromeos::FakeSessionManagerClient::Get()->set_arc_available(true);
    BrowserWithTestWindowTest::SetUp();

    profile()->GetPrefs()->SetBoolean(arc::prefs::kArcEnabled, true);
    arc_test_.set_wait_default_apps(true);
    arc_test_.SetUp(profile());
    // Set up ArcIntentHelperBridge to emulate full-duplex IntentHelper
    // connection.
    intent_helper_bridge_ = std::make_unique<arc::ArcIntentHelperBridge>(
        profile(), arc::ArcServiceManager::Get()->arc_bridge_service());
    arc::ArcServiceManager::Get()
        ->arc_bridge_service()
        ->intent_helper()
        ->SetInstance(&intent_helper_);
    WaitForInstanceReady(
        arc::ArcServiceManager::Get()->arc_bridge_service()->intent_helper());
  }

  void TearDown() override {
    arc::ArcServiceManager::Get()
        ->arc_bridge_service()
        ->intent_helper()
        ->CloseInstance(&intent_helper_);
    intent_helper_bridge_.reset();
    arc_test_.TearDown();
    BrowserWithTestWindowTest::TearDown();
    chromeos::SessionManagerClient::Shutdown();
  }

 protected:
  void CreateTab(bool started_from_arc) {
    AddTab(browser(), GURL("http://www.tests.com"));

    web_contents_ = browser()->tab_strip_model()->GetWebContentsAt(0);
    if (started_from_arc) {
      web_contents_->SetUserData(
          &arc::ArcWebContentsData::kArcTransitionFlag,
          std::make_unique<arc::ArcWebContentsData>(web_contents_));
    }
  }

  bool WasTabStartedFromArc() {
    return GetAndResetSafeToRedirectToArcWithoutUserConfirmationFlagForTesting(
        web_contents_);
  }

  MockSharingService* CreateSharingService() {
    return static_cast<MockSharingService*>(
        SharingServiceFactory::GetInstance()->SetTestingFactoryAndUse(
            profile(),
            base::BindRepeating([](content::BrowserContext* context) {
              return static_cast<std::unique_ptr<KeyedService>>(
                  std::make_unique<MockSharingService>());
            })));
  }

  content::WebContents* web_contents() { return web_contents_; }

 private:
  // Keep only one |WebContents| at a time.
  content::WebContents* web_contents_;
  arc::FakeIntentHelperInstance intent_helper_;
  ArcAppTest arc_test_;
  std::unique_ptr<arc::ArcIntentHelperBridge> intent_helper_bridge_;
};

const char* kChromePackageName =
    ArcIntentHelperBridge::kArcIntentHelperPackageName;

// Creates a dummy GurlAndActivityInfo object.
GurlAndActivityInfo CreateEmptyGurlAndActivityInfo() {
  return std::make_pair(GURL(), ArcIntentHelperBridge::ActivityName(
                                    /*package_name=*/std::string(),
                                    /*activity_name=*/std::string()));
}

// Creates and returns a new IntentHandlerInfo object.
mojom::IntentHandlerInfoPtr Create(const std::string& name,
                                   const std::string& package_name,
                                   const std::string& activity_name,
                                   bool is_preferred,
                                   const GURL& fallback_url) {
  mojom::IntentHandlerInfoPtr ptr = mojom::IntentHandlerInfo::New();
  ptr->name = name;
  ptr->package_name = package_name;
  ptr->activity_name = activity_name;
  ptr->is_preferred = is_preferred;
  if (!fallback_url.is_empty())
    ptr->fallback_url = fallback_url.spec();
  return ptr;
}

}  // namespace

// Tests that when one app is passed to GetAction but the user hasn't selected
// it and |in_out_safe_to_bypass_ui| is true, the function returns
// HANDLE_URL_IN_ARC.
TEST(ArcExternalProtocolDialogTest,
     TestGetActionWithOneAppBypassesIntentPicker) {
  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("package", "com.google.package.name",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, /*fallback_url=*/GURL()));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = true;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(GURL("external-protocol:foo"), handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_TRUE(in_out_safe_to_bypass_ui);
}

// Tests that when one app is passed to GetAction but the user hasn't selected
// it and |in_out_safe_to_bypass_ui| is false, the function returns
// ASK_USER.
TEST(ArcExternalProtocolDialogTest,
     TestGetActionWithOneAppDoesntBypassIntentPicker) {
  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("package", "com.google.package.name",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, /*fallback_url=*/GURL()));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::ASK_USER,
      GetActionForTesting(GURL("external-protocol:foo"), handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_FALSE(in_out_safe_to_bypass_ui);
}

// Tests that when 2+ apps are passed to GetAction but the user hasn't selected
// any the function returns ASK_USER, independently of whether or not is marked
// as safe to bypass the ui.
TEST(ArcExternalProtocolDialogTest,
     TestGetActionWithTwoAppWontBypassIntentPicker) {
  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("package", "com.google.package.name",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, /*fallback_url=*/GURL()));
  handlers.push_back(Create("package2", "com.google.package.name2",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, /*fallback_url=*/GURL()));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = true;
  EXPECT_EQ(
      GetActionResult::ASK_USER,
      GetActionForTesting(GURL("external-protocol:foo"), handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_FALSE(in_out_safe_to_bypass_ui);

  in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::ASK_USER,
      GetActionForTesting(GURL("external-protocol:foo"), handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_FALSE(in_out_safe_to_bypass_ui);
}

// Tests that when one preferred app is passed to GetAction, the function
// returns HANDLE_URL_IN_ARC even if the user hasn't selected the app, safe to
// bypass the UI is not relevant for this context.
TEST(ArcExternalProtocolDialogTest, TestGetActionWithOnePreferredApp) {
  const GURL external_url("external-protocol:foo");
  const std::string package_name("com.google.package.name");
  const std::string activity_name("com.google.activity");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("package", package_name, activity_name,
                            /*is_preferred=*/true,
                            /*fallback_url=*/GURL()));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();

  bool in_out_safe_to_bypass_ui = true;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(external_url, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(external_url, url_and_activity_name.first);
  EXPECT_EQ(package_name, url_and_activity_name.second.package_name);
  EXPECT_EQ(activity_name, url_and_activity_name.second.activity_name);
  EXPECT_TRUE(in_out_safe_to_bypass_ui);

  in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(external_url, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  // The flag was flipped since we have a preferred app.
  EXPECT_TRUE(in_out_safe_to_bypass_ui);
  EXPECT_EQ(external_url, url_and_activity_name.first);
  EXPECT_EQ(package_name, url_and_activity_name.second.package_name);
  EXPECT_EQ(activity_name, url_and_activity_name.second.activity_name);
}

// Tests that when one app is passed to GetAction, the user has already selected
// it, the function returns HANDLE_URL_IN_ARC. Since the user already selected
// safe to bypass ui it's always false.
TEST(ArcExternalProtocolDialogTest, TestGetActionWithOneAppSelected) {
  const GURL external_url("external-protocol:foo");
  const std::string package_name("com.google.package.name");
  const std::string activity_name("fake_activity_name");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("package", package_name, activity_name,
                            /*is_preferred=*/false,
                            /*fallback_url=*/GURL()));

  constexpr size_t kSelection = 0;
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = true;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(external_url, handlers, kSelection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(external_url, url_and_activity_name.first);
  EXPECT_EQ(package_name, url_and_activity_name.second.package_name);
  EXPECT_EQ(activity_name, url_and_activity_name.second.activity_name);
  EXPECT_FALSE(in_out_safe_to_bypass_ui);

  in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(external_url, handlers, kSelection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(external_url, url_and_activity_name.first);
  EXPECT_EQ(package_name, url_and_activity_name.second.package_name);
  EXPECT_EQ(activity_name, url_and_activity_name.second.activity_name);
  EXPECT_FALSE(in_out_safe_to_bypass_ui);
}

// Tests the same as TestGetActionWithOnePreferredApp but with two apps.
TEST(ArcExternalProtocolDialogTest,
     TestGetActionWithOnePreferredAppAndOneOther) {
  const GURL external_url("external-protocol:foo");
  const std::string package_name("com.google.package2.name");
  const std::string activity_name("fake_activity_name2");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("package", "com.google.package.name",
                            "fake_activity_name",
                            /*is_preferred=*/false, /*fallback_url=*/GURL()));
  handlers.push_back(Create("package2", package_name, activity_name,
                            /*is_preferred=*/true,
                            /*fallback_url=*/GURL()));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  // For cases with 2+ apps it doesn't matter whether it was marked as safe to
  // bypass or not, it will only check for user's preferrences.
  bool in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(external_url, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(external_url, url_and_activity_name.first);
  EXPECT_EQ(package_name, url_and_activity_name.second.package_name);
  EXPECT_EQ(activity_name, url_and_activity_name.second.activity_name);
  // It is expected to correct the flag to true, regardless of the initial
  // value, since there is a preferred app.
  EXPECT_TRUE(in_out_safe_to_bypass_ui);

  in_out_safe_to_bypass_ui = true;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(external_url, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(external_url, url_and_activity_name.first);
  EXPECT_EQ(package_name, url_and_activity_name.second.package_name);
  EXPECT_EQ(activity_name, url_and_activity_name.second.activity_name);
  EXPECT_TRUE(in_out_safe_to_bypass_ui);
}

// Tests that HANDLE_URL_IN_ARC is returned for geo: URL. The URL is special in
// that intent_helper (i.e. the Chrome proxy) can handle it but Chrome cannot.
// We have to send such a URL to intent_helper to let the helper rewrite the
// URL to https://maps.google.com/?latlon=xxx which Chrome can handle. Since the
// url needs to be fixed in ARC first, safe to bypass doesn't modify this
// behavior.
TEST(ArcExternalProtocolDialogTest, TestGetActionWithGeoUrl) {
  const GURL geo_url("geo:37.7749,-122.4194");

  const std::string activity_name("chrome_activity_name");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Chrome", kChromePackageName, activity_name,
                            /*is_preferred=*/true,
                            /*fallback_url=*/GURL()));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(geo_url, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(geo_url, url_and_activity_name.first);
  EXPECT_EQ(kChromePackageName, url_and_activity_name.second.package_name);
  EXPECT_EQ(activity_name, url_and_activity_name.second.activity_name);
  // Value will be corrected as in previous scenarios.
  EXPECT_TRUE(in_out_safe_to_bypass_ui);
}

// Tests that OPEN_URL_IN_CHROME is returned when a handler with a fallback http
// URL and kChromePackageName is passed to GetAction, even if the handler is not
// a preferred one.
TEST(ArcExternalProtocolDialogTest, TestGetActionWithOneFallbackUrl) {
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;"
      "S.browser_fallback_url=http://zxing.org;end");
  const GURL fallback_url("http://zxing.org");
  const std::string activity_name("fake_activity_name");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Chrome", kChromePackageName, activity_name,
                            /*is_preferred=*/false, fallback_url));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();

  // Since the navigation is intended to stay in Chrome the UI is bypassed.
  bool in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::OPEN_URL_IN_CHROME,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(fallback_url, url_and_activity_name.first);
  EXPECT_EQ(kChromePackageName, url_and_activity_name.second.package_name);
  EXPECT_EQ(activity_name, url_and_activity_name.second.activity_name);
  EXPECT_TRUE(in_out_safe_to_bypass_ui);

  in_out_safe_to_bypass_ui = true;
  EXPECT_EQ(
      GetActionResult::OPEN_URL_IN_CHROME,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(fallback_url, url_and_activity_name.first);
  EXPECT_EQ(kChromePackageName, url_and_activity_name.second.package_name);
  EXPECT_EQ(activity_name, url_and_activity_name.second.activity_name);
  EXPECT_TRUE(in_out_safe_to_bypass_ui);
}

// Tests the same with https and is_preferred == true.
TEST(ArcExternalProtocolDialogTest, TestGetActionWithOnePreferredFallbackUrl) {
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;"
      "S.browser_fallback_url=https://zxing.org;end");
  const GURL fallback_url("https://zxing.org");
  const std::string activity_name("fake_activity_name");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Chrome", kChromePackageName, activity_name,
                            /*is_preferred=*/true, fallback_url));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();

  // Safe to bypass should be marked as true in the end, since the
  // OPEN_URL_IN_CHROME actually bypasses the UI, regardless of the flag.
  bool in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::OPEN_URL_IN_CHROME,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(fallback_url, url_and_activity_name.first);
  EXPECT_EQ(kChromePackageName, url_and_activity_name.second.package_name);
  EXPECT_EQ(activity_name, url_and_activity_name.second.activity_name);
  EXPECT_TRUE(in_out_safe_to_bypass_ui);

  // Changing the flag will not modify the outcome.
  in_out_safe_to_bypass_ui = true;
  EXPECT_EQ(
      GetActionResult::OPEN_URL_IN_CHROME,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(fallback_url, url_and_activity_name.first);
  EXPECT_EQ(kChromePackageName, url_and_activity_name.second.package_name);
  EXPECT_EQ(activity_name, url_and_activity_name.second.activity_name);
  EXPECT_TRUE(in_out_safe_to_bypass_ui);
}

// Tests that ASK_USER is returned when two handlers with fallback URLs are
// passed to GetAction. This may happen when the user has installed a 3rd party
// browser app, and then clicks a intent: URI with a http fallback.
TEST(ArcExternalProtocolDialogTest, TestGetActionWithTwoFallbackUrls) {
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;"
      "S.browser_fallback_url=http://zxing.org;end");
  const GURL fallback_url("http://zxing.org");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Other browser", "com.other.browser",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, fallback_url));
  handlers.push_back(Create("Chrome", kChromePackageName,
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, fallback_url));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::ASK_USER,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_FALSE(in_out_safe_to_bypass_ui);
}

// Tests the same but set Chrome as a preferred app. In this case, ASK_USER
// shouldn't be returned.
TEST(ArcExternalProtocolDialogTest,
     TestGetActionWithTwoFallbackUrlsChromePreferred) {
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;"
      "S.browser_fallback_url=http://zxing.org;end");
  const GURL fallback_url("http://zxing.org");
  const std::string chrome_activity("chrome_activity");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Other browser", "com.other.browser",
                            "fake_activity",
                            /*is_preferred=*/false, fallback_url));
  handlers.push_back(Create("Chrome", kChromePackageName, chrome_activity,
                            /*is_preferred=*/true, fallback_url));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::OPEN_URL_IN_CHROME,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(fallback_url, url_and_activity_name.first);
  EXPECT_EQ(kChromePackageName, url_and_activity_name.second.package_name);
  EXPECT_EQ(chrome_activity, url_and_activity_name.second.activity_name);
  // Remember that this flag gets fixed under the presence of a preferred app.
  EXPECT_TRUE(in_out_safe_to_bypass_ui);
}

// Tests the same but set "other browser" as a preferred app. In this case,
// ASK_USER shouldn't be returned either.
TEST(ArcExternalProtocolDialogTest,
     TestGetActionWithTwoFallbackUrlsOtherBrowserPreferred) {
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;"
      "S.browser_fallback_url=http://zxing.org;end");
  const GURL fallback_url("http://zxing.org");
  const std::string package_name = "com.other.browser";
  const std::string chrome_activity_name("chrome_activity_name");
  const std::string other_activity_name("other_activity_name");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Other browser", package_name, other_activity_name,
                            /*is_preferred=*/true, fallback_url));
  handlers.push_back(Create("Chrome", kChromePackageName, chrome_activity_name,
                            /*is_preferred=*/false, fallback_url));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(fallback_url, url_and_activity_name.first);
  EXPECT_EQ(package_name, url_and_activity_name.second.package_name);
  EXPECT_EQ(other_activity_name, url_and_activity_name.second.activity_name);
  EXPECT_TRUE(in_out_safe_to_bypass_ui);
}

// Tests the same but set Chrome as a user-selected app.
TEST(ArcExternalProtocolDialogTest,
     TestGetActionWithTwoFallbackUrlsChromeSelected) {
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;"
      "S.browser_fallback_url=http://zxing.org;end");
  const GURL fallback_url("http://zxing.org");
  const std::string chrome_activity_name("chrome_activity");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Other browser", "com.other.browser",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, fallback_url));
  handlers.push_back(Create("Chrome", kChromePackageName, chrome_activity_name,
                            /*is_preferred=*/false, fallback_url));

  constexpr size_t kSelection = 1;  // Chrome
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = true;
  EXPECT_EQ(
      GetActionResult::OPEN_URL_IN_CHROME,
      GetActionForTesting(intent_url_with_fallback, handlers, kSelection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(fallback_url, url_and_activity_name.first);
  EXPECT_EQ(kChromePackageName, url_and_activity_name.second.package_name);
  EXPECT_EQ(chrome_activity_name, url_and_activity_name.second.activity_name);
  EXPECT_FALSE(in_out_safe_to_bypass_ui);
}

// Tests the same but set "other browser" as a preferred app.
TEST(ArcExternalProtocolDialogTest,
     TestGetActionWithTwoFallbackUrlsOtherBrowserSelected) {
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;"
      "S.browser_fallback_url=http://zxing.org;end");
  const GURL fallback_url("http://zxing.org");
  const std::string package_name = "com.other.browser";
  const std::string other_activity_name("other_activity_name");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Other browser", package_name, other_activity_name,
                            /*is_preferred=*/false, fallback_url));
  handlers.push_back(Create("Chrome", kChromePackageName, "chrome_activity",
                            /*is_preferred=*/false, fallback_url));

  constexpr size_t kSelection = 0;  // the other browser
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  // Already selected app index, output should be corrected to false.
  bool in_out_safe_to_bypass_ui = true;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(intent_url_with_fallback, handlers, kSelection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(fallback_url, url_and_activity_name.first);
  EXPECT_EQ(package_name, url_and_activity_name.second.package_name);
  EXPECT_EQ(other_activity_name, url_and_activity_name.second.activity_name);
  EXPECT_FALSE(in_out_safe_to_bypass_ui);
}

// Tests that HANDLE_URL_IN_ARC is returned when a handler with a fallback
// market: URL is passed to GetAction iff the flag to bypass the UI is set,
// otherwise UI will prompt to ASK_USER.
TEST(ArcExternalProtocolDialogTest, TestGetActionWithOneMarketFallbackUrl) {
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;end");
  const GURL fallback_url("market://details?id=com.google.abc");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Play Store", "com.google.play.store",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, fallback_url));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = true;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_TRUE(in_out_safe_to_bypass_ui);

  in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::ASK_USER,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_FALSE(in_out_safe_to_bypass_ui);
}

// Tests the same but with is_preferred == true.
TEST(ArcExternalProtocolDialogTest,
     TestGetActionWithOnePreferredMarketFallbackUrl) {
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;end");
  const GURL fallback_url("market://details?id=com.google.abc");
  const std::string play_store_package_name = "com.google.play.store";
  const std::string play_store_activity("play_store_activity");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Play Store", play_store_package_name,
                            play_store_activity,
                            /*is_preferred=*/true, fallback_url));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = true;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(fallback_url, url_and_activity_name.first);
  EXPECT_EQ(play_store_package_name, url_and_activity_name.second.package_name);
  EXPECT_EQ(play_store_activity, url_and_activity_name.second.activity_name);
  EXPECT_TRUE(in_out_safe_to_bypass_ui);

  in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(fallback_url, url_and_activity_name.first);
  EXPECT_EQ(play_store_package_name, url_and_activity_name.second.package_name);
  EXPECT_EQ(play_store_activity, url_and_activity_name.second.activity_name);
  EXPECT_TRUE(in_out_safe_to_bypass_ui);
}

// Tests the same but with an app_seleteced_index.
TEST(ArcExternalProtocolDialogTest,
     TestGetActionWithOneSelectedMarketFallbackUrl) {
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;end");
  const GURL fallback_url("market://details?id=com.google.abc");
  const std::string play_store_package_name = "com.google.play.store";
  const std::string play_store_activity("play_store_activity");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Play Store", play_store_package_name,
                            play_store_activity,
                            /*is_preferred=*/false, fallback_url));

  constexpr size_t kSelection = 0;
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  // App already selected, it doesn't really makes sense to call GetAction with
  // |in_out_safe_to_bypass_ui| set to true here.
  bool in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(intent_url_with_fallback, handlers, kSelection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(fallback_url, url_and_activity_name.first);
  EXPECT_EQ(play_store_package_name, url_and_activity_name.second.package_name);
  EXPECT_EQ(play_store_activity, url_and_activity_name.second.activity_name);
  EXPECT_FALSE(in_out_safe_to_bypass_ui);
}

// Tests that HANDLE_URL_IN_ARC is returned when a handler with a fallback
// market: URL is passed to GetAction.
TEST(ArcExternalProtocolDialogTest,
     TestGetActionWithOneMarketFallbackUrlBypassIntentPicker) {
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;end");
  const GURL fallback_url("market://details?id=com.google.abc");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Play Store", "com.google.play.store",
                            "play_store_activity", /*is_preferred=*/false,
                            fallback_url));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = true;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_TRUE(in_out_safe_to_bypass_ui);
}

// Tests that ASK_USER is returned when two handlers with fallback market: URLs
// are passed to GetAction. Unlike the two browsers case, this rarely happens on
// the user's device, though.
TEST(ArcExternalProtocolDialogTest, TestGetActionWithTwoMarketFallbackUrls) {
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;end");
  const GURL fallback_url("market://details?id=com.google.abc");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Play Store", "com.google.play.store",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, fallback_url));
  handlers.push_back(Create("Other Store app", "com.other.play.store",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, fallback_url));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::ASK_USER,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_FALSE(in_out_safe_to_bypass_ui);
}

// Tests the same, but make the second handler a preferred one.
TEST(ArcExternalProtocolDialogTest,
     TestGetActionWithTwoMarketFallbackUrlsOnePreferred) {
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;end");
  const GURL fallback_url("market://details?id=com.google.abc");
  const std::string play_store_package_name = "com.google.play.store";
  const std::string play_store_activity("play.store.act1");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Other Store app", "com.other.play.store",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, fallback_url));
  handlers.push_back(Create("Play Store", play_store_package_name,
                            play_store_activity,
                            /*is_preferred=*/true, fallback_url));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(fallback_url, url_and_activity_name.first);
  EXPECT_EQ(play_store_package_name, url_and_activity_name.second.package_name);
  EXPECT_EQ(play_store_activity, url_and_activity_name.second.activity_name);
  EXPECT_TRUE(in_out_safe_to_bypass_ui);
}

// Tests the same, but make the second handler a selected one.
TEST(ArcExternalProtocolDialogTest,
     TestGetActionWithTwoMarketFallbackUrlsOneSelected) {
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;end");
  const GURL fallback_url("market://details?id=com.google.abc");
  const std::string play_store_package_name = "com.google.play.store";
  const std::string play_store_activity("play.store.act1");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Other Store app", "com.other.play.store",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, fallback_url));
  handlers.push_back(Create("Play Store", play_store_package_name,
                            play_store_activity,
                            /*is_preferred=*/false, fallback_url));

  const size_t kSelection = 1;  // Play Store
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  // After selection doesn't really makes sense to check this value.
  bool in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(intent_url_with_fallback, handlers, kSelection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(fallback_url, url_and_activity_name.first);
  EXPECT_EQ(play_store_package_name, url_and_activity_name.second.package_name);
  EXPECT_EQ(play_store_activity, url_and_activity_name.second.activity_name);
}

// Tests the case where geo: URL is returned as a fallback. This should never
// happen because intent_helper ignores such a fallback, but just in case.
// GetAction shouldn't crash at least.
TEST(ArcExternalProtocolDialogTest, TestGetActionWithGeoUrlAsFallback) {
  // Note: geo: as a browser fallback is banned in the production code.
  const GURL intent_url_with_fallback(
      "intent://scan/#Intent;scheme=abc;package=com.google.abc;"
      "S.browser_fallback_url=geo:37.7749,-122.4194;end");
  const GURL geo_url("geo:37.7749,-122.4194");
  const std::string chrome_activity("chrome.activity");

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("Chrome", kChromePackageName, chrome_activity,
                            /*is_preferred=*/true, geo_url));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = false;
  // GetAction shouldn't return OPEN_URL_IN_CHROME because Chrome doesn't
  // directly support geo:.
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(intent_url_with_fallback, handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_EQ(geo_url, url_and_activity_name.first);
  EXPECT_EQ(kChromePackageName, url_and_activity_name.second.package_name);
  EXPECT_EQ(chrome_activity, url_and_activity_name.second.activity_name);
  EXPECT_TRUE(in_out_safe_to_bypass_ui);
}

// Test that GetUrlToNavigateOnDeactivate returns an empty GURL when |handlers|
// is empty.
TEST(ArcExternalProtocolDialogTest, TestGetUrlToNavigateOnDeactivateEmpty) {
  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  EXPECT_EQ(GURL(), GetUrlToNavigateOnDeactivateForTesting(handlers));
}

// Test that GetUrlToNavigateOnDeactivate returns an empty GURL when |handlers|
// only contains a (non-Chrome) app.
TEST(ArcExternalProtocolDialogTest, TestGetUrlToNavigateOnDeactivateAppOnly) {
  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  // On production, when |handlers| only contains app(s), the fallback field is
  // empty, but to make the test more reliable, use non-empty fallback URL.
  handlers.push_back(Create("App", "app.package",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, GURL("http://www")));
  EXPECT_EQ(GURL(), GetUrlToNavigateOnDeactivateForTesting(handlers));
}

// Test that GetUrlToNavigateOnDeactivate returns an empty GURL when |handlers|
// only contains (non-Chrome) apps.
TEST(ArcExternalProtocolDialogTest, TestGetUrlToNavigateOnDeactivateAppsOnly) {
  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  // On production, when |handlers| only contains app(s), the fallback field is
  // empty, but to make the test more reliable, use non-empty fallback URL.
  handlers.push_back(Create("App1", "app1.package",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, GURL("http://www")));
  handlers.push_back(Create("App2", "app2.package",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, GURL("http://www")));
  EXPECT_EQ(GURL(), GetUrlToNavigateOnDeactivateForTesting(handlers));
}

// Test that GetUrlToNavigateOnDeactivate returns an empty GURL when |handlers|
// contains Chrome, but it's not for http(s).
TEST(ArcExternalProtocolDialogTest, TestGetUrlToNavigateOnDeactivateGeoUrl) {
  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(
      Create("Chrome", kChromePackageName, /*activity_name=*/std::string(),
             /*is_preferred=*/false, GURL("geo:37.4220,-122.0840")));
  EXPECT_EQ(GURL(), GetUrlToNavigateOnDeactivateForTesting(handlers));
}

// Test that GetUrlToNavigateOnDeactivate returns non-empty GURL when |handlers|
// contains Chrome and an app.
TEST(ArcExternalProtocolDialogTest,
     TestGetUrlToNavigateOnDeactivateChromeAndApp) {
  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  // On production, all handlers have the same fallback URL, but to make sure
  // that "Chrome" is actually selected by the function, use different URLs.
  handlers.push_back(Create("A browser app", "browser.app.package",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, GURL("http://www1/")));
  handlers.push_back(Create("Chrome", kChromePackageName,
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, GURL("http://www2/")));
  handlers.push_back(Create("Yet another browser app",
                            "yet.another.browser.app.package",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, GURL("http://www3/")));

  EXPECT_EQ(GURL("http://www2/"),
            GetUrlToNavigateOnDeactivateForTesting(handlers));
}

// Does the same with https, just in case.
TEST(ArcExternalProtocolDialogTest,
     TestGetUrlToNavigateOnDeactivateChromeAndAppHttps) {
  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("A browser app", "browser.app.package",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, GURL("https://www1/")));
  handlers.push_back(Create("Chrome", kChromePackageName,
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, GURL("https://www2/")));
  handlers.push_back(Create("Yet another browser app",
                            "yet.another.browser.app.package",
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, GURL("https://www3/")));

  EXPECT_EQ(GURL("https://www2/"),
            GetUrlToNavigateOnDeactivateForTesting(handlers));
}

// Checks that the flag is correctly attached to the current tab.
TEST_F(ArcExternalProtocolDialogTestUtils, TestTabIsStartedFromArc) {
  CreateTab(/*started_from_arc=*/true);

  EXPECT_TRUE(WasTabStartedFromArc());
}

// Tests the same as the previous, just for when the data is not attached to the
// tab.
TEST_F(ArcExternalProtocolDialogTestUtils, TestTabIsNotStartedFromArc) {
  CreateTab(/*started_from_arc=*/false);

  EXPECT_FALSE(WasTabStartedFromArc());
}

// Tests that IsChromeAnAppCandidate works as intended.
TEST(ArcExternalProtocolDialogTest, TestIsChromeAnAppCandidate) {
  // First 3 cases are valid, just switching the position of Chrome.
  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(
      Create("fake app 1", "fake.app.package", /*activity_name=*/std::string(),
             /*is_preferred=*/false, GURL("https://www.fo.com")));
  handlers.push_back(
      Create("fake app 2", "fake.app.package2", /*activity_name=*/std::string(),
             /*is_preferred=*/false, GURL("https://www.bar.com")));
  handlers.push_back(Create("Chrome", kChromePackageName,
                            /*activity_name=*/std::string(),
                            /*is_preferred=*/false, GURL("https://www/")));
  EXPECT_TRUE(IsChromeAnAppCandidateForTesting(handlers));

  std::vector<mojom::IntentHandlerInfoPtr> handlers2;
  handlers2.push_back(
      Create("fake app 1", "fake.app.package", /*activity_name=*/std::string(),
             /*is_preferred=*/false, GURL("https://www.fo.com")));
  handlers2.push_back(Create("Chrome", kChromePackageName,
                             /*activity_name=*/std::string(),
                             /*is_preferred=*/false, GURL("https://www/")));
  handlers2.push_back(
      Create("fake app 2", "fake.app.package2", /*activity_name=*/std::string(),
             /*is_preferred=*/false, GURL("https://www.bar.com")));
  EXPECT_TRUE(IsChromeAnAppCandidateForTesting(handlers2));

  std::vector<mojom::IntentHandlerInfoPtr> handlers3;
  handlers3.push_back(Create("Chrome", kChromePackageName,
                             /*activity_name=*/std::string(),
                             /*is_preferred=*/false, GURL("https://www/")));
  handlers3.push_back(
      Create("fake app 1", "fake.app.package", /*activity_name=*/std::string(),
             /*is_preferred=*/false, GURL("https://www.fo.com")));
  handlers3.push_back(
      Create("fake app 2", "fake.app.package2", /*activity_name=*/std::string(),
             /*is_preferred=*/false, GURL("https://www.bar.com")));
  EXPECT_TRUE(IsChromeAnAppCandidateForTesting(handlers3));

  // Only non-Chrome apps.
  std::vector<mojom::IntentHandlerInfoPtr> handlers4;
  handlers4.push_back(
      Create("fake app 1", "fake.app.package", /*activity_name=*/std::string(),
             /*is_preferred=*/false, GURL("https://www.fo.com")));
  handlers4.push_back(
      Create("fake app 2", "fake.app.package2", /*activity_name=*/std::string(),
             /*is_preferred=*/false, GURL("https://www.bar.com")));
  handlers4.push_back(Create("fake app 3", "fake.app.package3",
                             /*activity_name=*/std::string(),
                             /*is_preferred=*/false, GURL("https://www/")));
  EXPECT_FALSE(IsChromeAnAppCandidateForTesting(handlers4));

  // Empty vector case.
  EXPECT_FALSE(IsChromeAnAppCandidateForTesting(
      std::vector<mojom::IntentHandlerInfoPtr>()));
}

// Tests that when one app is passed to GetAction and it's for ARC IME, the
// picker won't be triggered.
TEST(ArcExternalProtocolDialogTest,
     TestGetActionWithArcImeSettingsActivityBypassesIntentPicker) {
  constexpr char kPackageForOpeningArcImeSettingsPage[] =
      "org.chromium.arc.applauncher";
  constexpr char kActivityForOpeningArcImeSettingsPage[] =
      "org.chromium.arc.applauncher.InputMethodSettingsActivity";

  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  handlers.push_back(Create("ARC IME settings",
                            kPackageForOpeningArcImeSettingsPage,
                            kActivityForOpeningArcImeSettingsPage,
                            /*is_preferred=*/false, /*fallback_url=*/GURL()));

  const size_t no_selection = handlers.size();
  GurlAndActivityInfo url_and_activity_name = CreateEmptyGurlAndActivityInfo();
  bool in_out_safe_to_bypass_ui = false;
  EXPECT_EQ(
      GetActionResult::HANDLE_URL_IN_ARC,
      GetActionForTesting(GURL("intent:foo"), handlers, no_selection,
                          &url_and_activity_name, &in_out_safe_to_bypass_ui));
  EXPECT_TRUE(in_out_safe_to_bypass_ui);
}

MATCHER_P(ProtoEquals, message, "") {
  std::string expected_serialized, actual_serialized;
  message.SerializeToString(&expected_serialized);
  arg.SerializeToString(&actual_serialized);
  return expected_serialized == actual_serialized;
}

// Tests that clicking on a device calls through to SharingService.
TEST_F(ArcExternalProtocolDialogTestUtils, TestSelectDeviceForTelLink) {
  CreateTab(/*started_from_arc=*/false);

  std::string device_guid = "device_guid";
  MockSharingService* sharing_service = CreateSharingService();
  content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
  std::vector<mojom::IntentHandlerInfoPtr> handlers;
  std::vector<std::unique_ptr<syncer::DeviceInfo>> devices;
  devices.push_back(CreateFakeDeviceInfo(device_guid));

  GURL phone_number("tel:073%2099%209999%2099");

  chrome_browser_sharing::SharingMessage sharing_message;
  sharing_message.mutable_click_to_call_message()->set_phone_number(
      phone_number.GetContent());
  EXPECT_CALL(*sharing_service,
              SendMessageToDevice(
                  Property(&syncer::DeviceInfo::guid, device_guid), testing::_,
                  ProtoEquals(sharing_message), testing::_));

  OnIntentPickerClosedForTesting(
      rvh->GetProcess()->GetID(), rvh->GetRoutingID(), phone_number,
      /*safe_to_bypass_ui=*/true, std::move(handlers), std::move(devices),
      /*selected_app_package=*/device_guid, apps::PickerEntryType::kDevice,
      apps::IntentPickerCloseReason::OPEN_APP, /*should_persist=*/false);
}

TEST_F(ArcExternalProtocolDialogTestUtils, TestDialogWithoutAppsWithDevices) {
  CreateTab(/*started_from_arc=*/false);

  MockSharingService* sharing_service = CreateSharingService();
  content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
  std::vector<std::unique_ptr<syncer::DeviceInfo>> devices;
  devices.push_back(CreateFakeDeviceInfo("device_guid"));

  EXPECT_CALL(*sharing_service, GetDeviceCandidates(testing::_))
      .WillOnce(testing::Return(testing::ByMove(std::move(devices))));

  base::RunLoop run_loop;
  ClickToCallUiController::GetOrCreateFromWebContents(web_contents())
      ->set_on_dialog_shown_closure_for_testing(run_loop.QuitClosure());

  bool handled = false;
  arc::RunArcExternalProtocolDialog(
      GURL("tel:12341234"), /*initiating_origin=*/absl::nullopt,
      rvh->GetProcess()->GetID(), rvh->GetRoutingID(), ui::PAGE_TRANSITION_LINK,
      /*has_user_gesture=*/true,
      base::BindOnce([](bool* handled, bool result) { *handled = result; },
                     &handled));

  // Wait until the bubble is visible.
  run_loop.Run();
  EXPECT_TRUE(handled);
}

}  // namespace arc


text:
'use strict';

describe('Directive: cNavigationButton', function () {

  // load the directive's module
  beforeEach(module('frontendApp'));

  var element,
    scope;
    //not testing the callbacks
  // beforeEach(inject(function ($rootScope) {
  //   scope = $rootScope.$new();
  // }));

  // it('should make hidden element visible', inject(function ($compile) {
  //   element = angular.element('<c-navigation-button></c-navigation-button>');
  //   element = $compile(element)(scope);
  //   expect(element.text()).toBe('this is the cNavigationButton directive');
  // }));
});

redpajama_set_name: RedPajamaGithub


text:
<!DOCTYPE html>

<TITLE>Manpage of PAM_GROUP</TITLE>
<meta charset="utf-8">
<link rel="stylesheet" href="/css/main.css" type="text/css">

linux manpages

{"type":"documentation"}

PAM_GROUP

Section: Linux-PAM Manual (8)
Updated: 06/16/2009
Index
Return to Main Contents

pam_group - PAM module for group access

Synopsis

pam_group.so

DESCRIPTION

The pam_group PAM module does not authenticate the user, but instead it grants group memberships (in the credential setting phase of the authentication module) to the user. Such memberships are based on the service they are applying for.

By default rules for group memberships are taken from config file
FC/etc/security/group.confF[].

This module's usefulness relies on the file-systems accessible to the user. The point being that once granted the membership of a group, the user may attempt to create a
setgid
binary with a restricted group ownership. Later, when the user is not given membership to this group, they can recover group membership with the precompiled binary. The reason that the file-systems that the user has access to are so significant, is the fact that when a system is mounted
nosuid
the user is unable to create or execute such a binary file. For this module to provide any level of security, all file-systems that the user has write access to should be mounted
nosuid.

The pam_group module functions in parallel with the
FC/etc/groupF[]
file. If the user is granted any groups based on the behavior of this module, they are granted
in addition
to those entries
FC/etc/groupF[]
(or equivalent).

OPTIONS

This module does not recognise any options.

MODULE TYPES PROVIDED

Only the
auth
module type is provided.

RETURN VALUES

PAM_SUCCESS

group membership was granted.

PAM_ABORT

Not all relevant data could be gotten.

PAM_BUF_ERR

Memory buffer error.

PAM_CRED_ERR

Group membership was not granted.

PAM_IGNORE

pam_sm_authenticate
was called which does nothing.

PAM_USER_UNKNOWN

The user is not known to the system.

FILES

FC/etc/security/group.confF[]

Default configuration file

SEE ALSO

group.conf(5),
pam.d(5),
pam(8).

AUTHORS

pam_group was written by Andrew G. Morgan <morgan@kernel.org>.

Index

Synopsis

DESCRIPTION

OPTIONS

MODULE TYPES PROVIDED

RETURN VALUES

FILES

SEE ALSO

AUTHORS

This document was created by
man2html,
using the manual pages.

Time: 05:34:28 GMT, December 24, 2015


text:
// Copyright 2017 The Crashpad Authors. 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.

#include "util/linux/scoped_ptrace_attach.h"

#include <errno.h>
#include <sys/ptrace.h>
#include <unistd.h>

#include "gtest/gtest.h"
#include "test/errors.h"
#include "test/multiprocess.h"
#include "util/file/file_io.h"
#include "util/linux/scoped_pr_set_ptracer.h"

namespace crashpad {
namespace test {
namespace {

// If Yama is not enabled, the default ptrace restrictions should be
// sufficient for these tests.
//
// If Yama is enabled, then /proc/sys/kernel/yama/ptrace_scope must be 0
// (YAMA_SCOPE_DISABLED) or 1 (YAMA_SCOPE_RELATIONAL) for these tests to
// succeed. If it is 2 (YAMA_SCOPE_CAPABILITY) then the test requires
// CAP_SYS_PTRACE, and if it is 3 (YAMA_SCOPE_NO_ATTACH), these tests will fail.

class AttachTest : public Multiprocess {
 public:
  AttachTest() : Multiprocess() {}

  AttachTest(const AttachTest&) = delete;
  AttachTest& operator=(const AttachTest&) = delete;

  ~AttachTest() {}

 protected:
  const long kWord = 42;
};

class AttachToChildTest : public AttachTest {
 public:
  AttachToChildTest() : AttachTest() {}

  AttachToChildTest(const AttachToChildTest&) = delete;
  AttachToChildTest& operator=(const AttachToChildTest&) = delete;

  ~AttachToChildTest() {}

 private:
  void MultiprocessParent() override {
    // Wait for the child to set the parent as its ptracer.
    char c;
    CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c));

    pid_t pid = ChildPID();

    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);
    EXPECT_EQ(errno, ESRCH) << ErrnoMessage("ptrace");

    ScopedPtraceAttach attachment;
    ASSERT_EQ(attachment.ResetAttach(pid), true);
    EXPECT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), kWord)
        << ErrnoMessage("ptrace");
    attachment.Reset();

    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);
    EXPECT_EQ(errno, ESRCH) << ErrnoMessage("ptrace");
  }

  void MultiprocessChild() override {
    ScopedPrSetPtracer set_ptracer(getppid(), /* may_log= */ true);

    char c = '\0';
    CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));

    CheckedReadFileAtEOF(ReadPipeHandle());
  }
};

TEST(ScopedPtraceAttach, AttachChild) {
  AttachToChildTest test;
  test.Run();
}

class AttachToParentResetTest : public AttachTest {
 public:
  AttachToParentResetTest() : AttachTest() {}

  AttachToParentResetTest(const AttachToParentResetTest&) = delete;
  AttachToParentResetTest& operator=(const AttachToParentResetTest&) = delete;

  ~AttachToParentResetTest() {}

 private:
  void MultiprocessParent() override {
    ScopedPrSetPtracer set_ptracer(ChildPID(), /* may_log= */ true);
    char c = '\0';
    CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));

    CheckedReadFileAtEOF(ReadPipeHandle());
  }

  void MultiprocessChild() override {
    // Wait for the parent to set the child as its ptracer.
    char c;
    CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c));

    pid_t pid = getppid();

    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);
    EXPECT_EQ(errno, ESRCH) << ErrnoMessage("ptrace");

    ScopedPtraceAttach attachment;
    ASSERT_EQ(attachment.ResetAttach(pid), true);
    EXPECT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), kWord)
        << ErrnoMessage("ptrace");
    attachment.Reset();

    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);
    EXPECT_EQ(errno, ESRCH) << ErrnoMessage("ptrace");
  }
};

TEST(ScopedPtraceAttach, AttachParentReset) {
  AttachToParentResetTest test;
  test.Run();
}

class AttachToParentDestructorTest : public AttachTest {
 public:
  AttachToParentDestructorTest() : AttachTest() {}

  AttachToParentDestructorTest(const AttachToParentDestructorTest&) = delete;
  AttachToParentDestructorTest& operator=(const AttachToParentDestructorTest&) =
      delete;

  ~AttachToParentDestructorTest() {}

 private:
  void MultiprocessParent() override {
    ScopedPrSetPtracer set_ptracer(ChildPID(), /* may_log= */ true);
    char c = '\0';
    CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));

    CheckedReadFileAtEOF(ReadPipeHandle());
  }

  void MultiprocessChild() override {
    // Wait for the parent to set the child as its ptracer.
    char c;
    CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c));

    pid_t pid = getppid();
    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);
    EXPECT_EQ(errno, ESRCH) << ErrnoMessage("ptrace");
    {
      ScopedPtraceAttach attachment;
      ASSERT_EQ(attachment.ResetAttach(pid), true);
      EXPECT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), kWord)
          << ErrnoMessage("ptrace");
    }
    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);
    EXPECT_EQ(errno, ESRCH) << ErrnoMessage("ptrace");
  }
};

TEST(ScopedPtraceAttach, AttachParentDestructor) {
  AttachToParentDestructorTest test;
  test.Run();
}

}  // namespace
}  // namespace test
}  // namespace crashpad


text:
package com.xiaomo.main.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.nutz.dao.Cnd;
import org.nutz.dao.Condition;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.pager.Pager;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import com.xiaoleilu.hutool.util.StrUtil;
import com.xiaomo.main.bean.AreaInfo;
import com.xiaomo.main.bean.PoliceInfo;
import com.xiaomo.main.pojo.EasyUiJsonObj;

@IocBean
public class AreaInfoService {

	 @Inject
	 protected Dao dao;

		private static final Log log = Logs.get();

	 public EasyUiJsonObj<AreaInfo> getInfoByPage(Integer pageNumber,Integer pageSize
			 ){
		 EasyUiJsonObj<AreaInfo>  easyUiJsonObj = new EasyUiJsonObj<AreaInfo>();
		 Pager page = dao.createPager(pageNumber, pageSize);
//		 List<SqlExpressionGroup> queryParam = new ArrayList<SqlExpressionGroup>();
//		 if (StrUtil.isNotBlank(policeId)) {
//			 queryParam.add(Cnd.exps("policeId", "LIKE", "%".concat(policeId).concat("%")));
//		 }
//		 if (StrUtil.isNotBlank(name)) {
//			 queryParam.add(Cnd.exps("name", "LIKE", "%".concat(name).concat("%")));
//		 }
//		 if (StrUtil.isNotBlank(position)) {
//			 queryParam.add(Cnd.exps("policeId", "LIKE", "%".concat(position).concat("%")));
//		 }
//		 if (StrUtil.isNotBlank(telephone)) {
//			 queryParam.add(Cnd.exps("telephone", "LIKE", "%".concat(telephone).concat("%")));
//		 }
//		 Cnd c = null;
		 Condition condition = null;
//		 if (queryParam.size() >= 1) {
//			for (int i = 0; i < queryParam.size(); i++) {
//				if (i == 0) {
//					 c = Cnd.where(queryParam.get(i));
//				}else{
//					c.and(queryParam.get(i));
//				}
//			}
//			condition = c.desc("id");
//		 }else{
			 condition =  Cnd.orderBy().desc("id");
//		 }

		 List<AreaInfo> list = dao.query(AreaInfo.class, condition ,page);
		 easyUiJsonObj.setTotal(dao.count(AreaInfo.class,condition));
		 easyUiJsonObj.setRows(list);
		 return easyUiJsonObj;
	 }

	 public List<String> getAllArea(){
		 List<AreaInfo> ai = dao.query(AreaInfo.class, null ,null);
		 List<String> ls = new ArrayList<String>();
		 for (int i = 0 ,len = ai.size() ; i < len; i++) {
			ls.add(ai.get(i).getArea());
		}
		 return ls;
	 }

	 public NutMap areaInfoSave( AreaInfo ai ){
		 boolean result = false;
		 if (ai.getArId() == null) {
			 ai.setCreateTime(new Date());
			 ai.setUpdateTime(new Date());
			 ai = dao.insert(ai);
			 if (ai != null) {
				result = true;
			}
		}else{
			ai.setUpdateTime(new Date());;
			result = dao.updateIgnoreNull(ai) > 0;
		}
		 if (result ) {
			 return new NutMap().addv("success", "true");
		 }else{
			 return new NutMap().addv("errorMsg", "保存数据出错,请联系管理员!");
		 }
	 }

		public NutMap areaInfoDelete(String delIds) {
	    	Sql sql = Sqls.create("delete from t_area_info where id in("+delIds+") ");
	    	sql = dao.execute(sql);
	    	 return new NutMap().addv("success", "true");
		}

}


text:
bool ShouldClearOrderTargeting();

int SelectCommandLength(const uint8_t *data);

void RemoveFromHotkeyGroups(Unit *unit);

void Command_Select(const uint8_t *buf);
void Command_SelectionAdd(const uint8_t *buf);
void Command_SelectionRemove(const uint8_t *buf);

void SendChangeSelectionCommand(int count, Unit **units);
void CenterOnSelectionGroup(uint8_t group_id);
void SelectHotkeyGroup(uint8_t group_id);
void Command_SaveHotkeyGroup(int group, bool shift_add);
void Command_LoadHotkeyGroup(uint8_t group_id);
int TrySelectRecentHotkeyGroup(Unit *unit);
void StatusScreenButton(Control *clicked_button);

class Unit;

class Selection
{
    public:
        class iterator
        {
            public:
                iterator(Unit **addr) : value(addr) {}
                Unit *operator*() { return *value; }
                bool operator==(const iterator &other) { return other.value == value; }
                bool operator!=(const iterator &other) { return other.value != value; }
                iterator &operator++() { value++; return *this; }
                Unit **value;
        };

        iterator begin() { return iterator(addr); }
        iterator end() { return iterator(addr + Count()); }
        int Find(Unit *unit);

        Selection(array_offset<Unit *, 12> address) : addr((Unit **)address.raw_pointer()) {}
        Unit *Get(int pos);
        Unit *operator[](int pos) { return Get(pos); }

        int Count() { int count = Find(0); if (count < 0) return Limits::Selection; return count; }

    private:
        Unit **addr;
};

extern Selection client_select;
extern Selection client_select3;
extern Selection selections[8];

#endif // SELECTION_H


text:
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.flags;

import java.util.List;

/**
 * @author freva
 */
public class ListFlag<T> extends FlagImpl<List<T>, ListFlag<T>> {
    public ListFlag(FlagId id, List<T> defaultValue, FetchVector vector, FlagSerializer<List<T>> serializer, FlagSource source) {
        super(id, defaultValue, vector, serializer, source, ListFlag::new);
    }

    @Override
    public ListFlag<T> self() {
        return this;
    }

    public List<T> value() {
        return boxedValue();
    }
}


text:
package org.apache.tez.test;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.fs.FileContext;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.LocalFileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.mapred.ShuffleHandler;
import org.apache.hadoop.mapreduce.v2.jobhistory.JobHistoryUtils;
import org.apache.hadoop.service.Service;
import org.apache.hadoop.util.JarFinder;
import org.apache.hadoop.yarn.api.records.ApplicationId;
import org.apache.hadoop.yarn.api.records.ApplicationReport;
import org.apache.hadoop.yarn.api.records.YarnApplicationState;
import org.apache.hadoop.yarn.client.api.YarnClient;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.server.MiniYARNCluster;
import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor;
import org.apache.hadoop.yarn.server.nodemanager.DefaultContainerExecutor;
import org.apache.tez.dag.api.TezConfiguration;
import org.apache.tez.dag.api.TezUncheckedException;
import org.apache.tez.dag.app.DAGAppMaster;
import org.apache.tez.mapreduce.hadoop.MRConfig;
import org.apache.tez.mapreduce.hadoop.MRJobConfig;

import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;

/**
 * Configures and starts the Tez-specific components in the YARN cluster.
 *
 */
public class MiniTezCluster extends MiniYARNCluster {

  public static final String APPJAR = JarFinder.getJar(DAGAppMaster.class);

  private static final Logger LOG = LoggerFactory.getLogger(MiniTezCluster.class);

  private static final String YARN_CLUSTER_CONFIG = "yarn-site.xml";

  private Path confFilePath;

  private long maxTimeToWaitForAppsOnShutdown;

  public MiniTezCluster(String testName) {
    this(testName, 1);
  }

  public MiniTezCluster(String testName, int noOfNMs) {
    super(testName, noOfNMs, 4, 4);
  }

  public MiniTezCluster(String testName, int noOfNMs,
      int numLocalDirs, int numLogDirs)  {
    super(testName, noOfNMs, numLocalDirs, numLogDirs);
  }

  @Override
  public void init(Configuration conf) {
    if (conf.getFloat(YarnConfiguration.NM_MAX_PER_DISK_UTILIZATION_PERCENTAGE,
        YarnConfiguration.DEFAULT_NM_MAX_PER_DISK_UTILIZATION_PERCENTAGE) == YarnConfiguration.DEFAULT_NM_MAX_PER_DISK_UTILIZATION_PERCENTAGE) {
      conf.setFloat(YarnConfiguration.NM_MAX_PER_DISK_UTILIZATION_PERCENTAGE, 99.0f);
    }
    super.init(conf);
  }

  @Override
  public void serviceInit(Configuration conf) throws Exception {
    conf.set(MRConfig.FRAMEWORK_NAME, MRConfig.YARN_TEZ_FRAMEWORK_NAME);
    // Use libs from cluster since no build is available
    conf.setBoolean(TezConfiguration.TEZ_USE_CLUSTER_HADOOP_LIBS, true);
    // blacklisting disabled to prevent scheduling issues
    conf.setBoolean(TezConfiguration.TEZ_AM_NODE_BLACKLISTING_ENABLED, false);
    if (conf.get(MRJobConfig.MR_AM_STAGING_DIR) == null) {
      conf.set(MRJobConfig.MR_AM_STAGING_DIR, new File(getTestWorkDir(),
          "apps_staging_dir" + Path.SEPARATOR).getAbsolutePath());
    }

    if (conf.get(YarnConfiguration.DEBUG_NM_DELETE_DELAY_SEC) == null) {
      // nothing defined. set quick delete value
      conf.setLong(YarnConfiguration.DEBUG_NM_DELETE_DELAY_SEC, 0l);
    }

    maxTimeToWaitForAppsOnShutdown = conf.getLong(
        TezConfiguration.TEZ_TEST_MINI_CLUSTER_APP_WAIT_ON_SHUTDOWN_SECS,
        TezConfiguration.TEZ_TEST_MINI_CLUSTER_APP_WAIT_ON_SHUTDOWN_SECS_DEFAULT);

    File appJarLocalFile = new File(MiniTezCluster.APPJAR);

    if (!appJarLocalFile.exists()) {
      String message = "TezAppJar " + MiniTezCluster.APPJAR
          + " not found. Exiting.";
      LOG.info(message);
      throw new TezUncheckedException(message);
    } else {
      LOG.info("Using Tez AppJar: " + appJarLocalFile.getAbsolutePath());
    }

    FileSystem fs = FileSystem.get(conf);
    Path testRootDir = fs.makeQualified(new Path("target", getName() + "-tmpDir"));
    Path appRemoteJar = new Path(testRootDir, "TezAppJar.jar");
    // Copy AppJar and make it public.
    Path appMasterJar = new Path(MiniTezCluster.APPJAR);
    fs.copyFromLocalFile(appMasterJar, appRemoteJar);
    fs.setPermission(appRemoteJar, new FsPermission("777"));

    conf.set(TezConfiguration.TEZ_LIB_URIS, appRemoteJar.toUri().toString());
    LOG.info("Set TEZ-LIB-URI to: " + conf.get(TezConfiguration.TEZ_LIB_URIS));

    // VMEM monitoring disabled, PMEM monitoring enabled.
    conf.setBoolean(YarnConfiguration.NM_PMEM_CHECK_ENABLED, false);
    conf.setBoolean(YarnConfiguration.NM_VMEM_CHECK_ENABLED, false);

    conf.set(CommonConfigurationKeys.FS_PERMISSIONS_UMASK_KEY,  "000");
    conf.setInt(CommonConfigurationKeys.IPC_CLIENT_CONNECT_MAX_RETRIES_KEY, 1);
    conf.setInt(CommonConfigurationKeys.IPC_CLIENT_CONNECT_TIMEOUT_KEY, 1000);
    conf.setInt(CommonConfigurationKeysPublic.IPC_CLIENT_CONNECT_MAX_RETRIES_KEY,0);
    conf.setInt(CommonConfigurationKeysPublic.IPC_CLIENT_CONNECT_MAX_RETRIES_ON_SOCKET_TIMEOUTS_KEY, 0);
    conf.setInt(CommonConfigurationKeysPublic.IPC_CLIENT_CONNECT_TIMEOUT_KEY,1000);

    try {
      Path stagingPath = FileContext.getFileContext(conf).makeQualified(
          new Path(conf.get(MRJobConfig.MR_AM_STAGING_DIR)));
      /*
       * Re-configure the staging path on Windows if the file system is localFs.
       * We need to use a absolute path that contains the drive letter. The unit
       * test could run on a different drive than the AM. We can run into the
       * issue that job files are localized to the drive where the test runs on,
       * while the AM starts on a different drive and fails to find the job
       * metafiles. Using absolute path can avoid this ambiguity.
       */
      if (Path.WINDOWS) {
        if (LocalFileSystem.class.isInstance(stagingPath.getFileSystem(conf))) {
          conf.set(MRJobConfig.MR_AM_STAGING_DIR,
              new File(conf.get(MRJobConfig.MR_AM_STAGING_DIR))
                  .getAbsolutePath());
        }
      }
      FileContext fc=FileContext.getFileContext(stagingPath.toUri(), conf);
      if (fc.util().exists(stagingPath)) {
        LOG.info(stagingPath + " exists! deleting...");
        fc.delete(stagingPath, true);
      }
      LOG.info("mkdir: " + stagingPath);
      fc.mkdir(stagingPath, null, true);

      //mkdir done directory as well
      String doneDir =
          JobHistoryUtils.getConfiguredHistoryServerDoneDirPrefix(conf);
      Path doneDirPath = fc.makeQualified(new Path(doneDir));
      fc.mkdir(doneDirPath, null, true);
    } catch (IOException e) {
      throw new TezUncheckedException("Could not create staging directory. ", e);
    }
    conf.set(MRConfig.MASTER_ADDRESS, "test");

    //configure the shuffle service in NM
    if (conf.get(YarnConfiguration.NM_AUX_SERVICES) == null) {
      conf.setStrings(YarnConfiguration.NM_AUX_SERVICES,
          new String[]{ShuffleHandler.MAPREDUCE_SHUFFLE_SERVICEID});
      conf.setClass(String.format(YarnConfiguration.NM_AUX_SERVICE_FMT,
          ShuffleHandler.MAPREDUCE_SHUFFLE_SERVICEID), ShuffleHandler.class,
          Service.class);
      // Non-standard shuffle port
      conf.setInt(ShuffleHandler.SHUFFLE_PORT_CONFIG_KEY, 0);
    }
    conf.setClass(YarnConfiguration.NM_CONTAINER_EXECUTOR,
        DefaultContainerExecutor.class, ContainerExecutor.class);

    // TestMRJobs is for testing non-uberized operation only; see TestUberAM
    // for corresponding uberized tests.
    conf.setBoolean(MRJobConfig.JOB_UBERTASK_ENABLE, false);
    super.serviceInit(conf);
  }

  @Override
  public void serviceStart() throws Exception {
    LOG.info("Starting MiniTezCluster");
    super.serviceStart();
    File workDir = super.getTestWorkDir();
    Configuration conf = super.getConfig();

    confFilePath = new Path(workDir.getAbsolutePath(), YARN_CLUSTER_CONFIG);
    File confFile = new File(confFilePath.toString());
    try {
      confFile.createNewFile();
      conf.writeXml(new FileOutputStream(confFile));
      confFile.deleteOnExit();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      throw new RuntimeException(e);
    }
    confFilePath = new Path(confFile.getAbsolutePath());
    conf.setStrings(YarnConfiguration.YARN_APPLICATION_CLASSPATH,
        workDir.getAbsolutePath(), System.getProperty("java.class.path"));
    LOG.info("Setting yarn-site.xml via YARN-APP-CP at: "
        + conf.get(YarnConfiguration.YARN_APPLICATION_CLASSPATH));
  }

  @Override
  protected void serviceStop() throws Exception {
    waitForAppsToFinish();
    super.serviceStop();
  }

  private void waitForAppsToFinish() {
    long waitStartTime = System.currentTimeMillis();
    long waitEndTime = maxTimeToWaitForAppsOnShutdown == -1 ?
        -1 : (waitStartTime + (1000 * maxTimeToWaitForAppsOnShutdown));

    YarnClient yarnClient = YarnClient.createYarnClient();
    yarnClient.init(getConfig());
    yarnClient.start();
    Collection<ApplicationReport> unCompletedApps = null;
    try {
      do {
        List<ApplicationReport> appReports = yarnClient.getApplications();
        unCompletedApps = Collections2.filter(appReports, new Predicate<ApplicationReport>() {
          @Override
          public boolean apply(ApplicationReport appReport) {
            return EnumSet.of(YarnApplicationState.NEW, YarnApplicationState.NEW_SAVING,
                YarnApplicationState.SUBMITTED, YarnApplicationState.ACCEPTED, YarnApplicationState.RUNNING)
                .contains(appReport.getYarnApplicationState());
          }
        });
        if (unCompletedApps.isEmpty()) {
          break;
        }
        LOG.info("Waiting for applications to finish in MiniTezCluster"
            + ", incompleteAppsCount=" + unCompletedApps.size());
        Thread.sleep(1000);
      } while (waitEndTime != -1 && waitEndTime > System.currentTimeMillis());

      if (unCompletedApps != null && !unCompletedApps.isEmpty()) {
        LOG.info("Killing incomplete applications in MiniTezCluster"
            + ", incompleteAppsCount=" + unCompletedApps.size());
        Set<ApplicationId> incompleteAppIds =
            new HashSet<ApplicationId>();
        for (ApplicationReport appReport : unCompletedApps) {
          try {
            LOG.info("Killing application, id=" + appReport.getApplicationId()
                + ", appName=" + appReport.getName());
            yarnClient.killApplication(appReport.getApplicationId());
            incompleteAppIds.add(appReport.getApplicationId());
          } catch (Exception e) {
            LOG.warn("Failed to kill app on MiniTezCluster shutdown"
                + ", appId=" + appReport.getApplicationId()
                + ", appName=" + appReport.getName());
          }
        }

        // Wait for RM to report back that incomplete apps are killed
        waitStartTime = System.currentTimeMillis();
        waitEndTime = maxTimeToWaitForAppsOnShutdown == -1 ?
            -1 : (waitStartTime + (1000 * maxTimeToWaitForAppsOnShutdown));
        do {
          Iterator<ApplicationId> iter = incompleteAppIds.iterator();
          while (iter.hasNext()) {
            ApplicationId applicationId = iter.next();
            ApplicationReport report = yarnClient.getApplicationReport(applicationId);
            if (EnumSet.of(YarnApplicationState.FINISHED, YarnApplicationState.FAILED,
                YarnApplicationState.KILLED).contains(report.getYarnApplicationState())) {
              iter.remove();
              LOG.info("Application completed, id=" + report.getApplicationId()
                  + ", yarnState=" + report.getYarnApplicationState());
            }
          }
          if (incompleteAppIds.isEmpty()) {
            break;
          }
        } while (waitEndTime != -1 && waitEndTime > System.currentTimeMillis());

      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      yarnClient.stop();
    }
  }

  public Path getConfigFilePath() {
    return confFilePath;
  }

}


text:
using std::stack;
using std::string;
using std::vector;

namespace gmml {

GlycamParser::ParseInfo *GlycamParser::get_parse_info(
        const string& sequence) const {
    // This is populated with the parsed residues in the order they appear
    // in the sequence.
    vector<ParsedResidue*> *residues = new vector<ParsedResidue*>;

    // This sequence of tokens is used to discover the tree structure of the
    // sequence.
    vector<TokenType> *tokens = new vector<TokenType>;

    // This was added to differentiate between brackets that denote
    // derivatives and brackets that denote branches.
    bool reading_residue = true;

    // This marks the start of the residue that's currently being read.
    int start_index = 0;

    // First, the sequence is split into tokens and the residues are parsed
    // in the order they appear in the sequence.
    for (int i = 0; i < sequence.size(); i++) {
        switch (sequence[i]) {
            case '-': {
                bool next_is_terminal = false;
                int end_index = i + 1;
                // If the next character in the sequence is a number, it
                // indicates the position of the oxygen that this residue is
                // attached to and is parsed along with the residue.
                // Otherwise, it indicates that the next residue is the aglycon.
                if (!is_number(sequence[i + 1])) {
                    next_is_terminal = true;
                    end_index--;
                }
                string residue = sequence.substr(start_index,
                                                 end_index - start_index + 1);
                residues->push_back(parse_residue(residue));
                if (next_is_terminal) {
                    start_index = i + 1;
                } else {
                    // Skip the oxygen position, we parsed it along with the
                    // residue.
                    start_index = i + 2;
                    i++;
                }
                tokens->push_back(kTokenResidue);
                reading_residue = false;
                break;
            }
            case '[':
                if (!reading_residue) {
                    tokens->push_back(kTokenLeft);
                    start_index = i + 1;
                }
                break;
            case ']':
                if (!reading_residue) {
                    tokens->push_back(kTokenRight);
                    start_index = i + 1;
                }
                break;
            default:
                reading_residue = true;
                break;
        }
    }
    string terminal = sequence.substr(sequence.find_last_of('-') + 1);
    if (terminal != "") {
        residues->push_back(parse_residue(terminal));
        tokens->push_back(kTokenResidue);
    }

    return new ParseInfo(residues, tokens);
}

ArrayTree<ParsedResidue*> *GlycamParser::get_array_tree(
        const string& sequence) const {
    ParseInfo *parse_info = get_parse_info(sequence);
    vector<ParsedResidue*> *residues = parse_info->residues;
    vector<TokenType> *tokens = parse_info->tokens;

    vector<TokenType>::reverse_iterator cur_token = tokens->rbegin();
    vector<ParsedResidue*>::reverse_iterator cur_residue = residues->rbegin();

    ArrayTree<ParsedResidue*> *tree = new ArrayTree<ParsedResidue*>;
    if (residues->size() == 0) {
        delete parse_info;
        delete residues;
        delete tokens;
        return tree;
    }

    stack<int> st;
    st.push(tree->insert(*cur_residue));

    while (++cur_token != tokens->rend()) {
        switch (*cur_token) {
            case kTokenLeft:
                if (st.empty())
                    throw ParseException("Invalid branching in sequence");
                st.pop();
                break;
            case kTokenRight:
                ++cur_token;
                ++cur_residue;
                if (cur_residue == residues->rend())
                    throw ParseException("Invalid sequence of residues");
                if (st.empty())
                    throw ParseException("Invalid sequence");
                st.push(tree->insert(*cur_residue, st.top()));
                break;
            case kTokenResidue: {
                ++cur_residue;
                if (cur_residue == residues->rend())
                    throw ParseException("Invalid sequence of residues");
                if (st.empty())
                    throw ParseException("Invalid sequence");
                int parent = tree->insert(*cur_residue, st.top());
                st.pop();
                st.push(parent);
                break;
            }
        }
    }

    delete residues;
    delete tokens;
    delete parse_info;

    return tree;
}

tree<ParsedResidue*> *GlycamParser::parse(const string& sequence) const {
    ParseInfo *parse_info = get_parse_info(sequence);
    vector<ParsedResidue*> *residues = parse_info->residues;
    vector<TokenType> *tokens = parse_info->tokens;

    vector<TokenType>::reverse_iterator cur_token = tokens->rbegin();
    vector<ParsedResidue*>::reverse_iterator cur_residue = residues->rbegin();

    tree<ParsedResidue*> *tr = new tree<ParsedResidue*>;

    if (residues->size() == 0) {
        delete parse_info;
        delete residues;
        delete tokens;
        return tr;
    }

    // The top of the stack is the residue we're currently attaching to.
    stack<tree<ParsedResidue*>::iterator> st;

    // Set the root of the tree to be the last residue in the sequence.
    st.push(tr->insert(tr->begin(), *cur_residue));

    while (++cur_token != tokens->rend()) {
        switch (*cur_token) {
            case kTokenLeft:
                st.pop();
                break;
            case kTokenRight:
                ++cur_token;
                ++cur_residue;
                st.push(tr->append_child(st.top(), *cur_residue));
                break;
            case kTokenResidue: {
                ++cur_residue;
                tree<ParsedResidue*>::iterator it =
                    tr->append_child(st.top(), *cur_residue);
                st.pop();
                st.push(it);
                break;
            }
        }
    }

    delete residues;
    delete tokens;
    delete parse_info;

    return tr;
}

ParsedResidue *GlycamParser::parse_residue(const string& residue_string) const {
    ParsedResidue *residue = new ParsedResidue;

    size_t dash_index = residue_string.find('-');
    // If there's no dash, it must be the terminal
    if (dash_index == string::npos) {
        residue->name = residue_string;
        residue->is_terminal = true;
        return residue;
    }

    if (residue_string.empty())
        throw ParseException("Invalid residue in sequence");
    char isomer_letter = residue_string[0];
    if (isomer_letter == 'D' || isomer_letter == 'd')
        residue->isomer = ResidueClassification::kIsomerD;
    else if (isomer_letter = 'L' || isomer_letter == 'l')
        residue->isomer = ResidueClassification::kIsomerL;
    else
        throw ParseException("Invalid isomer in residue " + residue_string);

    if (dash_index <= 2)
        throw ParseException("Invalid residue in sequence");
    char config_letter = residue_string[dash_index - 2];
    if (config_letter == 'A' || config_letter == 'a')
        residue->configuration = ResidueClassification::kAlpha;
    else if (config_letter == 'B' || config_letter == 'b')
        residue->configuration = ResidueClassification::kBeta;
    else
        throw ParseException("Invalid isomer in residue " + residue_string);

    if (!is_number(residue_string[dash_index - 1]))
        throw ParseException("Invalid residue in sequence");
    residue->anomeric_carbon = char_to_number(residue_string[dash_index - 1]);
    if (dash_index != residue_string.size() - 1) {
        if (!is_number(residue_string[dash_index + 1]))
            throw ParseException("Invalid residue in sequence");
        residue->oxygen_position =
            char_to_number(residue_string[dash_index + 1]);
    } else {
        // If the dash is the last character, the terminal is next.
        residue->oxygen_position = 1;
    }

    size_t l_brack = residue_string.find('[');
    size_t r_brack = residue_string.find(']');
    if (l_brack == string::npos || r_brack == string::npos) {
        residue->name = residue_string.substr(1, dash_index - 3);
    } else {
        residue->name = residue_string.substr(1, l_brack - 1);
        if (parse_derivatives_) {
            string derivatives = residue_string.substr(l_brack + 1,
                                                       r_brack - l_brack - 1);
            vector<string> derivatives_tokens;
            split(derivatives, ',', derivatives_tokens);
            for (vector<string>::iterator it = derivatives_tokens.begin();
                    it != derivatives_tokens.end(); ++it) {
                if (!is_number(it->at(0))) {
                    throw ParseException(
                            "Invalid derivative position in sequence");
                }
                residue->derivatives[char_to_number(it->at(0))] =
                        it->substr(1);
            }
        }
    }

    return residue;
}

}  // namespace gmml


text:
using Orchard.Environment.Extensions;
using Orchard.Localization;
using Orchard.UI.Navigation;
using Orchard.Security;

namespace Orchard.Packaging {
    [OrchardFeature("Gallery")]
    public class AdminMenu : INavigationProvider {
        public Localizer T { get; set; }

        public string MenuName {
            get { return "admin"; }
        }

        public void GetNavigation(NavigationBuilder builder) {
            builder
                .Add(T("Modules"), menu => menu
                    .Add(T("Gallery"), "3", item => Describe(item, "Modules", "Gallery", true)))
                .Add(T("Themes"), menu => menu
                    .Add(T("Gallery"), "3", item => Describe(item, "Themes", "Gallery", true)))
                .Add(T("Settings"), menu => menu
                    .Add(T("Gallery"), "1", item => Describe(item, "Sources", "Gallery", false)));
        }

        static NavigationItemBuilder Describe(NavigationItemBuilder item, string actionName, string controllerName, bool localNav) {
            item = item.Action(actionName, controllerName, new { area = "Orchard.Packaging" }).Permission(StandardPermissions.SiteOwner);
            if (localNav)
                item = item.LocalNav();
            return item;
        }
    }
}


text:
package com.cool.dao;

import java.util.List;

import com.cool.base.BaseMapper;
import com.cool.model.SysUserMenu;

public interface SysUserMenuMapper extends BaseMapper<SysUserMenu> {
	/**
	 *
	* @Title: selectByUserId
	* @Description: 根据UserId查询
	* @param @param UserId
	* @param @return
	* @return SysUserMenu
	* @throws
	 */
	public List<SysUserMenu> selectByUserId(Long UserId);
	/**
	 *
	* @Title: selectAllByUserIds
	* @Description: 根据UserId查询
	* @param @param list
	* @param @return
	* @return List<SysUserMenu>
	* @throws
	 */
	public List<SysUserMenu> selectAllByUserIds(List<Long> list);
	/**
	 *
	* @Title: deleteAllByUserIds
	* @Description: 根据UserIds删除
	* @param @param list
	* @return void
	* @throws
	 */
	public void deleteAllByUserIds(List<Long> list);
	/**
	 *
	* @Title: deleteAllByMenuIds
	* @Description: 根据MenuIds删除
	* @param @param list
	* @return void
	* @throws
	 */
	public void deleteAllByMenuIds(List<Long> list);
}


text:
By submitting your Pull Request you agree to the [Contributor License Agreement](./amida_CLA.pdf).

When you contribute code, you affirm that the contribution is your original work and that you license the work to the project under the project's open source license. By submitting any copyrighted material via pull request, email, or other means you agree to license the material under the project's open source license and warrant that you have the legal authority to do so.

### Non-trivial Contributions (e.g. >100 lines of code, or new functionality)

We require a signed CLA prior to reviewing and merging the Pull Request. Please complete and sign, then scan and email a pdf file of the [Contributor License Agreement](./amida_CLA.pdf) to <tech@amida-tech.com>.

======

# How to contribute

## Create an issue
* Make sure it isn't a duplicate of an existing issue.
* Include system details.
* Paste any error output logs.

## Submit a Pull Request

1. Fork and clone the repo.
2. Create a branch for your changes.
3. Write tests for your changes.
4. Make sure all tests pass.
5. Submit a Pull Request.

Some things that will increase the chance that your pull request is accepted:

* Write good tests.
* Write a good commit message.
====