text:
package raw

import "fmt"
import "strings"
import "strconv"

// Indicates that a password hash or stub is invalid.
var ErrInvalidStub = fmt.Errorf("invalid stub")

// Indicates that the number of rounds specified is not in the valid range.
var ErrInvalidRounds = fmt.Errorf("invalid number of rounds")

// Scans a sha256-crypt or sha512-crypt modular crypt stub or modular crypt hash
// to determine configuration parameters.
func Parse(stub string) (isSHA512 bool, salt, hash string, rounds int, err error) {
	// $5$
	if len(stub) < 3 || stub[0] != '$' || stub[2] != '$' {
		err = ErrInvalidStub
		return
	}

	if stub[1] == '6' {
		isSHA512 = true
	} else if stub[1] != '5' {
		err = ErrInvalidStub
		return
	}

	rest := stub[3:]
	parts := strings.Split(rest, "$")
	roundsStr := ""

	switch len(parts) {
	case 1:
		// $5$
		// $5$salt
		salt = parts[0]
	case 2:
		// $5$salt$hash
		// $5$rounds=1000$salt
		if strings.HasPrefix(parts[0], "rounds=") {
			roundsStr = parts[0]
			salt = parts[1]
		} else {
			salt = parts[0]
			hash = parts[1]
		}
	case 3:
		// $5$rounds=1000$salt$hash
		roundsStr = parts[0]
		salt = parts[1]
		hash = parts[2]
	default:
		err = ErrInvalidStub
	}

	if roundsStr != "" {
		if !strings.HasPrefix(roundsStr, "rounds=") {
			err = ErrInvalidStub
			return
		}

		roundsStr = roundsStr[7:]
		var n uint64
		n, err = strconv.ParseUint(roundsStr, 10, 31)
		if err != nil {
			err = ErrInvalidStub
			return
		}

		rounds = int(n)

		if rounds < MinimumRounds || rounds > MaximumRounds {
			err = ErrInvalidRounds
			return
		}
	} else {
		rounds = DefaultRounds
	}

	return
}


text:
/* eslint-disable no-console */
import React, { Component } from 'react'
import moment from 'moment'

import Timeline, {
  TimelineMarkers,
  TimelineHeaders,
  TodayMarker,
  CustomMarker,
  CursorMarker,
  CustomHeader,
  SidebarHeader,
  DateHeader
} from 'react-calendar-timeline'

import generateFakeData from '../generate-fake-data'

var minTime = moment()
  .add(-6, 'months')
  .valueOf()
var maxTime = moment()
  .add(6, 'months')
  .valueOf()

var keys = {
  groupIdKey: 'id',
  groupTitleKey: 'title',
  groupRightTitleKey: 'rightTitle',
  itemIdKey: 'id',
  itemTitleKey: 'title',
  itemDivTitleKey: 'title',
  itemGroupKey: 'group',
  itemTimeStartKey: 'start',
  itemTimeEndKey: 'end'
}

export default class App extends Component {
  constructor(props) {
    super(props)

    const { groups, items } = generateFakeData()
    const defaultTimeStart = moment()
      .startOf('day')
      .toDate()
    const defaultTimeEnd = moment()
      .startOf('day')
      .add(1, 'day')
      .toDate()

    this.state = {
      groups,
      items,
      defaultTimeStart,
      defaultTimeEnd,
      selected: undefined,
    }
  }

  handleCanvasClick = (groupId, time) => {
    console.log('Canvas clicked', groupId, moment(time).format())
  }

  handleCanvasDoubleClick = (groupId, time) => {
    console.log('Canvas double clicked', groupId, moment(time).format())
  }

  handleCanvasContextMenu = (group, time) => {
    console.log('Canvas context menu', group, moment(time).format())
  }

  handleItemClick = (itemId, _, time) => {
    console.log('Clicked: ' + itemId, moment(time).format())
  }

  handleItemSelect = (itemId, _, time) => {
    this.setState({
      selected: [itemId]
    })
    console.log('Selected: ' + itemId, moment(time).format())
  }

  handleItemDeselect = () => {
    this.setState({selected: undefined})
  }

  handleItemDoubleClick = (itemId, _, time) => {
    console.log('Double Click: ' + itemId, moment(time).format())
  }

  handleItemContextMenu = (itemId, _, time) => {
    console.log('Context Menu: ' + itemId, moment(time).format())
  }

  handleItemMove = (itemId, dragTime, newGroupOrder) => {
    const { items, groups } = this.state

    const group = groups[newGroupOrder]

    this.setState({
      items: items.map(
        item =>
          item.id === itemId
            ? Object.assign({}, item, {
                start: dragTime,
                end: dragTime + (item.end - item.start),
                group: group.id
              })
            : item
      )
    })

    console.log('Moved', itemId, dragTime, newGroupOrder)
  }

  handleItemResize = (itemId, time, edge) => {
    const { items } = this.state

    this.setState({
      items: items.map(
        item =>
          item.id === itemId
            ? Object.assign({}, item, {
                start: edge === 'left' ? time : item.start,
                end: edge === 'left' ? item.end : time
              })
            : item
      )
    })

    console.log('Resized', itemId, time, edge)
  }

  // this limits the timeline to -6 months ... +6 months
  handleTimeChange = (visibleTimeStart, visibleTimeEnd, updateScrollCanvas) => {
    if (visibleTimeStart < minTime && visibleTimeEnd > maxTime) {
      updateScrollCanvas(minTime, maxTime)
    } else if (visibleTimeStart < minTime) {
      updateScrollCanvas(minTime, minTime + (visibleTimeEnd - visibleTimeStart))
    } else if (visibleTimeEnd > maxTime) {
      updateScrollCanvas(maxTime - (visibleTimeEnd - visibleTimeStart), maxTime)
    } else {
      updateScrollCanvas(visibleTimeStart, visibleTimeEnd)
    }
  }

  moveResizeValidator = (action, item, time) => {
    if (time < new Date().getTime()) {
      var newTime =
        Math.ceil(new Date().getTime() / (15 * 60 * 1000)) * (15 * 60 * 1000)
      return newTime
    }

    return time
  }

  render() {
    const { groups, items, defaultTimeStart, defaultTimeEnd } = this.state

    return (
      <Timeline
        groups={groups}
        items={items}
        keys={keys}
        sidebarWidth={150}
        sidebarContent={
Above The Left
}
        canMove
        canResize="right"
        canSelect
        itemsSorted
        itemTouchSendsClick={false}
        stackItems
        itemHeightRatio={0.75}
        defaultTimeStart={defaultTimeStart}
        defaultTimeEnd={defaultTimeEnd}
        onCanvasClick={this.handleCanvasClick}
        onCanvasDoubleClick={this.handleCanvasDoubleClick}
        onCanvasContextMenu={this.handleCanvasContextMenu}
        onItemClick={this.handleItemClick}
        onItemSelect={this.handleItemSelect}
        onItemContextMenu={this.handleItemContextMenu}
        onItemMove={this.handleItemMove}
        onItemResize={this.handleItemResize}
        onItemDoubleClick={this.handleItemDoubleClick}
        onTimeChange={this.handleTimeChange}
        moveResizeValidator={this.moveResizeValidator}
        selected={this.state.selected}
        onItemDeselect={this.handleItemDeselect}
      >
        <TimelineMarkers>
          <TodayMarker />
          <CustomMarker
            date={
              moment()
                .startOf('day')
                .valueOf() +
              1000 * 60 * 60 * 2
            }
          />
          <CustomMarker
            date={moment()
              .add(3, 'day')
              .valueOf()}
          >
            {({ styles }) => {
              const newStyles = { ...styles, backgroundColor: 'blue' }
              return

            }}
          </CustomMarker>
          <CursorMarker />
        </TimelineMarkers>
      </Timeline>
    )
  }
}

redpajama_set_name: RedPajamaGithub


text:
using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geodatabase;

namespace ESRI.ArcGIS.Samples.SimplePointPlugin
{
  public partial class OpenSimplePointDlg : Form
  {
    #region class memebers
    private IHookHelper m_hookHelper = null;
    IWorkspace m_workspace = null;
    #endregion

    #region class constructor
    public OpenSimplePointDlg(IHookHelper hookHelper)
    {
      if (null == hookHelper)
        throw new Exception("Hook helper is not initialize");

      InitializeComponent();

      m_hookHelper = hookHelper;
    }
    #endregion

    #region UI event handlers
    private void btnOpenDataSource_Click(object sender, EventArgs e)
    {
      m_workspace = OpenPlugInWorkspace();

      ListFeatureClasses();
    }

    private void lstDeatureClasses_DoubleClick(object sender, EventArgs e)
    {
      this.Hide();
      OpenDataset();
      this.Close();
    }

    private void btnOK_Click(object sender, EventArgs e)
    {
      this.Hide();
      OpenDataset();
      this.Close();
    }

    private void btnCancel_Click(object sender, EventArgs e)
    {
      this.Close();
    }
    #endregion

    #region private methods
    private string GetFileName()
    {
      OpenFileDialog dlg = new OpenFileDialog();
      dlg.Filter = "Simple Point (*.csp)|*.csp";
      dlg.Title = "Open Simple Point file";
      dlg.RestoreDirectory = true;
      dlg.CheckPathExists = true;
      dlg.CheckFileExists = true;
      dlg.Multiselect = false;

      DialogResult dr = dlg.ShowDialog();
      if (DialogResult.OK == dr)
        return dlg.FileName;

      return string.Empty;
    }

    private IWorkspace OpenPlugInWorkspace()
    {
      try
      {
        string path = GetFileName();
        if (string.Empty == path)
          return null;

        //update the path textbox
        txtPath.Text = path;

        //get the type using the ProgID
        Type t = Type.GetTypeFromProgID("esriGeoDatabase.SimplePointPluginWorkspaceFactory");
        //Use activator in order to create an instance of the workspace factory
        IWorkspaceFactory workspaceFactory = Activator.CreateInstance(t) as IWorkspaceFactory;

        //open the workspace
        return workspaceFactory.OpenFromFile(System.IO.Path.GetDirectoryName(path), 0);
      }
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message);
        return null;
      }
    }

    private void ListFeatureClasses()
    {
      lstDeatureClasses.Items.Clear();

      if (null == m_workspace)
        return;

      IEnumDatasetName datasetNames = m_workspace.get_DatasetNames(esriDatasetType.esriDTAny);
      datasetNames.Reset();
      IDatasetName dsName;
      while ((dsName = datasetNames.Next()) != null)
      {
        lstDeatureClasses.Items.Add(dsName.Name);
      }

      //select the first dataset on the list
      if (lstDeatureClasses.Items.Count > 0)
      {
        lstDeatureClasses.SelectedIndex = 0;
        lstDeatureClasses.Refresh();
      }
    }

    private void OpenDataset()
    {
      try
      {
        if (null == m_hookHelper || null == m_workspace)
          return;

        if (string.Empty == (string)lstDeatureClasses.SelectedItem)
          return;

        //get the selected item from the listbox
        string dataset = (string)lstDeatureClasses.SelectedItem;

        //cast the workspace into a feature workspace
        IFeatureWorkspace featureWorkspace = m_workspace as IFeatureWorkspace;
        if (null == featureWorkspace)
          return;

        //get a featureclass from the workspace
        IFeatureClass featureClass = featureWorkspace.OpenFeatureClass(dataset);

        //create a new feature layer and add it to the map
        IFeatureLayer featureLayer = new FeatureLayerClass();
        featureLayer.Name = featureClass.AliasName;
        featureLayer.FeatureClass = featureClass;
        m_hookHelper.FocusMap.AddLayer((ILayer)featureLayer);
      }
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message);
      }
    }
    #endregion
  }
}


text:
// Copyright (c) 2012 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/extensions/menu_manager.h"

#include <memory>
#include <utility>
#include <vector>

#include "base/bind.h"
#include "base/files/scoped_temp_dir.h"
#include "base/json/json_reader.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/extension_system_factory.h"
#include "chrome/browser/extensions/menu_manager.h"
#include "chrome/browser/extensions/menu_manager_test_observer.h"
#include "chrome/browser/extensions/test_extension_prefs.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/api/context_menus.h"
#include "chrome/test/base/testing_profile.h"
#include "components/prefs/pref_service.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "content/public/browser/context_menu_params.h"
#include "content/public/browser/notification_service.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/event_router_factory.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/state_store.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/context_menu/context_menu.mojom.h"

using testing::_;
using testing::AtLeast;
using testing::DeleteArg;
using testing::InSequence;
using testing::Return;
using testing::SaveArg;

namespace extensions {

namespace context_menus = api::context_menus;

// Base class for tests.
class MenuManagerTest : public testing::Test {
 public:
  MenuManagerTest()
      : profile_(new TestingProfile()),
        manager_(profile_.get(),
                 ExtensionSystem::Get(profile_.get())->state_store()),
        prefs_(base::ThreadTaskRunnerHandle::Get()),
        next_id_(1) {}

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

  void TearDown() override {
    prefs_.pref_service()->CommitPendingWrite();
    base::RunLoop().RunUntilIdle();
  }

  // Returns a test item.
  std::unique_ptr<MenuItem> CreateTestItem(const Extension* extension,
                                           bool incognito = false) {
    MenuItem::Type type = MenuItem::NORMAL;
    MenuItem::ContextList contexts(MenuItem::ALL);
    const MenuItem::ExtensionKey key(extension->id());
    MenuItem::Id id(incognito, key);
    id.uid = next_id_++;
    return std::make_unique<MenuItem>(id, "test", false, true, true, type,
                                      contexts);
  }

  // Returns a test item with the given string ID.
  std::unique_ptr<MenuItem> CreateTestItemWithID(const Extension* extension,
                                                 const std::string& string_id) {
    MenuItem::Type type = MenuItem::NORMAL;
    MenuItem::ContextList contexts(MenuItem::ALL);
    const MenuItem::ExtensionKey key(extension->id());
    MenuItem::Id id(false, key);
    id.string_uid = string_id;
    return std::make_unique<MenuItem>(id, "test", false, true, true, type,
                                      contexts);
  }

  // Creates and returns a test Extension. The caller does *not* own the return
  // value.
  const Extension* AddExtension(const std::string& name) {
    scoped_refptr<Extension> extension = prefs_.AddExtension(name);
    extensions_.push_back(extension);
    return extension.get();
  }

 protected:
  content::BrowserTaskEnvironment task_environment_;
  std::unique_ptr<TestingProfile> profile_;

  MenuManager manager_;
  ExtensionList extensions_;
  TestExtensionPrefs prefs_;
  int next_id_;
};

// Tests adding, getting, and removing items.
TEST_F(MenuManagerTest, AddGetRemoveItems) {
  const Extension* extension = AddExtension("test");

  // Add a new item, make sure you can get it back.
  std::unique_ptr<MenuItem> item1 = CreateTestItem(extension);
  ASSERT_TRUE(item1 != NULL);
  MenuItem* item1_ptr = item1.get();
  ASSERT_TRUE(manager_.AddContextItem(extension, std::move(item1)));
  ASSERT_EQ(item1_ptr, manager_.GetItemById(item1_ptr->id()));
  const MenuItem::OwnedList* items =
      manager_.MenuItems(item1_ptr->id().extension_key);
  ASSERT_EQ(1u, items->size());
  ASSERT_EQ(item1_ptr, items->at(0).get());

  // Add a second item, make sure it comes back too.
  std::unique_ptr<MenuItem> item2 = CreateTestItemWithID(extension, "id2");
  MenuItem* item2_ptr = item2.get();
  ASSERT_TRUE(manager_.AddContextItem(extension, std::move(item2)));
  ASSERT_EQ(item2_ptr, manager_.GetItemById(item2_ptr->id()));
  items = manager_.MenuItems(item2_ptr->id().extension_key);
  ASSERT_EQ(2u, items->size());
  ASSERT_EQ(item1_ptr, items->at(0).get());
  ASSERT_EQ(item2_ptr, items->at(1).get());

  // Try adding item 3, then removing it.
  std::unique_ptr<MenuItem> item3 = CreateTestItem(extension);
  MenuItem* item3_ptr = item3.get();
  MenuItem::Id id3 = item3_ptr->id();
  const MenuItem::ExtensionKey extension_key3(item3_ptr->id().extension_key);
  ASSERT_TRUE(manager_.AddContextItem(extension, std::move(item3)));
  ASSERT_EQ(item3_ptr, manager_.GetItemById(id3));
  ASSERT_EQ(3u, manager_.MenuItems(extension_key3)->size());
  ASSERT_TRUE(manager_.RemoveContextMenuItem(id3));
  ASSERT_EQ(NULL, manager_.GetItemById(id3));
  ASSERT_EQ(2u, manager_.MenuItems(extension_key3)->size());

  // Make sure removing a non-existent item returns false.
  const MenuItem::ExtensionKey key(extension->id());
  MenuItem::Id id(false, key);
  id.uid = id3.uid + 50;
  ASSERT_FALSE(manager_.RemoveContextMenuItem(id));

  // Make sure adding an item with the same string ID returns false.
  std::unique_ptr<MenuItem> item2too = CreateTestItemWithID(extension, "id2");
  ASSERT_FALSE(manager_.AddContextItem(extension, std::move(item2too)));

  // But the same string ID should not collide with another extension.
  const Extension* extension2 = AddExtension("test2");
  std::unique_ptr<MenuItem> item2other =
      CreateTestItemWithID(extension2, "id2");
  ASSERT_TRUE(manager_.AddContextItem(extension2, std::move(item2other)));
}

// Test adding/removing child items.
TEST_F(MenuManagerTest, ChildFunctions) {
  const Extension* extension1 = AddExtension("1111");
  const Extension* extension2 = AddExtension("2222");
  const Extension* extension3 = AddExtension("3333");

  std::unique_ptr<MenuItem> item1 = CreateTestItem(extension1);
  MenuItem* item1_ptr = item1.get();
  std::unique_ptr<MenuItem> item2 = CreateTestItem(extension2);
  MenuItem* item2_ptr = item2.get();
  std::unique_ptr<MenuItem> item2_child =
      CreateTestItemWithID(extension2, "2child");
  MenuItem* item2_child_ptr = item2_child.get();
  std::unique_ptr<MenuItem> item2_grandchild = CreateTestItem(extension2);
  std::unique_ptr<MenuItem> item3 = CreateTestItem(extension3);

  // Add in the first two items.
  ASSERT_TRUE(manager_.AddContextItem(extension1, std::move(item1)));
  ASSERT_TRUE(manager_.AddContextItem(extension2, std::move(item2)));

  MenuItem::Id id1 = item1_ptr->id();
  MenuItem::Id id2 = item2_ptr->id();

  // Try adding item3 as a child of item2 - this should fail because item3 has
  // a different extension id.
  ASSERT_FALSE(manager_.AddChildItem(id2, std::move(item3)));

  // Add item2_child as a child of item2.
  MenuItem::Id id2_child = item2_child->id();
  ASSERT_TRUE(manager_.AddChildItem(id2, std::move(item2_child)));
  ASSERT_EQ(1u, item2_ptr->children().size());
  ASSERT_EQ(0u, item1_ptr->children().size());
  ASSERT_EQ(item2_child_ptr, manager_.GetItemById(id2_child));

  ASSERT_EQ(1u, manager_.MenuItems(item1_ptr->id().extension_key)->size());
  ASSERT_EQ(item1_ptr,
            manager_.MenuItems(item1_ptr->id().extension_key)->at(0).get());

  // Add item2_grandchild as a child of item2_child, then remove it.
  MenuItem::Id id2_grandchild = item2_grandchild->id();
  ASSERT_TRUE(manager_.AddChildItem(id2_child, std::move(item2_grandchild)));
  ASSERT_EQ(1u, item2_ptr->children().size());
  ASSERT_EQ(1u, item2_child_ptr->children().size());
  ASSERT_TRUE(manager_.RemoveContextMenuItem(id2_grandchild));

  // We should only get 1 thing back when asking for item2's extension id, since
  // it has a child item.
  ASSERT_EQ(1u, manager_.MenuItems(item2_ptr->id().extension_key)->size());
  ASSERT_EQ(item2_ptr,
            manager_.MenuItems(item2_ptr->id().extension_key)->at(0).get());

  // Remove child2_item.
  ASSERT_TRUE(manager_.RemoveContextMenuItem(id2_child));
  ASSERT_EQ(1u, manager_.MenuItems(item2_ptr->id().extension_key)->size());
  ASSERT_EQ(item2_ptr,
            manager_.MenuItems(item2_ptr->id().extension_key)->at(0).get());
  ASSERT_EQ(0u, item2_ptr->children().size());
}

TEST_F(MenuManagerTest, PopulateFromValue) {
  const Extension* extension = AddExtension("test");
  std::string error;

  std::unique_ptr<MenuItem> invalid_item(MenuItem::Populate(
      extension->id(), base::Value("needs a dictionary"), &error));
  EXPECT_EQ(invalid_item.get(), nullptr);

  bool incognito = true;
  int type = MenuItem::CHECKBOX;
  std::string title("TITLE");
  bool checked = true;
  bool visible = true;
  bool enabled = true;
  MenuItem::ContextList contexts;
  contexts.Add(MenuItem::PAGE);
  contexts.Add(MenuItem::SELECTION);
  int contexts_value = contexts.ToValue()->GetInt();

  base::Value document_url_patterns(base::Value::Type::LIST);
  document_url_patterns.Append("http://www.google.com/*");
  document_url_patterns.Append("http://www.reddit.com/*");

  base::Value target_url_patterns(base::Value::Type::LIST);
  target_url_patterns.Append("http://www.yahoo.com/*");
  target_url_patterns.Append("http://www.facebook.com/*");

  base::Value value(base::Value::Type::DICTIONARY);
  value.SetBoolKey("incognito", incognito);
  value.SetStringKey("string_uid", std::string());
  value.SetIntKey("type", type);
  value.SetStringKey("title", title);
  value.SetBoolKey("checked", checked);
  value.SetBoolKey("visible", visible);
  value.SetBoolKey("enabled", enabled);
  value.SetIntKey("contexts", contexts_value);
  URLPatternSet document_url_pattern_set;
  document_url_pattern_set.Populate(
      base::Value::AsListValue(document_url_patterns), URLPattern::SCHEME_ALL,
      true, &error);
  value.SetKey("document_url_patterns", std::move(document_url_patterns));
  URLPatternSet target_url_pattern_set;
  target_url_pattern_set.Populate(base::Value::AsListValue(target_url_patterns),
                                  URLPattern::SCHEME_ALL, true, &error);
  value.SetKey("target_url_patterns", std::move(target_url_patterns));

  std::unique_ptr<MenuItem> item(
      MenuItem::Populate(extension->id(), value, &error));
  ASSERT_TRUE(item.get());

  EXPECT_EQ(extension->id(), item->extension_id());
  EXPECT_EQ(incognito, item->incognito());
  EXPECT_EQ(title, item->title());
  EXPECT_EQ(checked, item->checked());
  EXPECT_EQ(item->checked(), item->checked());
  EXPECT_EQ(visible, item->visible());
  EXPECT_EQ(enabled, item->enabled());
  EXPECT_EQ(contexts, item->contexts());

  EXPECT_EQ(document_url_pattern_set, item->document_url_patterns());

  EXPECT_EQ(target_url_pattern_set, item->target_url_patterns());
}

// Tests that deleting a parent properly removes descendants.
TEST_F(MenuManagerTest, DeleteParent) {
  const Extension* extension = AddExtension("1111");

  // Set up 5 items to add.
  std::unique_ptr<MenuItem> item1 = CreateTestItem(extension);
  std::unique_ptr<MenuItem> item2 = CreateTestItem(extension);
  std::unique_ptr<MenuItem> item3 = CreateTestItemWithID(extension, "id3");
  std::unique_ptr<MenuItem> item4 = CreateTestItemWithID(extension, "id4");
  std::unique_ptr<MenuItem> item5 = CreateTestItem(extension);
  std::unique_ptr<MenuItem> item6 = CreateTestItem(extension);
  MenuItem* item1_ptr = item1.get();
  MenuItem* item2_ptr = item2.get();
  MenuItem* item3_ptr = item3.get();
  MenuItem* item4_ptr = item4.get();
  MenuItem* item5_ptr = item5.get();
  MenuItem* item6_ptr = item6.get();
  MenuItem::Id item1_id = item1->id();
  MenuItem::Id item2_id = item2->id();
  MenuItem::Id item3_id = item3->id();
  MenuItem::Id item4_id = item4->id();
  MenuItem::Id item5_id = item5->id();
  MenuItem::Id item6_id = item6->id();
  const MenuItem::ExtensionKey key(extension->id());

  // Add the items in the hierarchy
  // item1 -> item2 -> item3 -> item4 -> item5 -> item6.
  ASSERT_TRUE(manager_.AddContextItem(extension, std::move(item1)));
  ASSERT_TRUE(manager_.AddChildItem(item1_id, std::move(item2)));
  ASSERT_TRUE(manager_.AddChildItem(item2_id, std::move(item3)));
  ASSERT_TRUE(manager_.AddChildItem(item3_id, std::move(item4)));
  ASSERT_TRUE(manager_.AddChildItem(item4_id, std::move(item5)));
  ASSERT_TRUE(manager_.AddChildItem(item5_id, std::move(item6)));
  ASSERT_EQ(item1_ptr, manager_.GetItemById(item1_id));
  ASSERT_EQ(item2_ptr, manager_.GetItemById(item2_id));
  ASSERT_EQ(item3_ptr, manager_.GetItemById(item3_id));
  ASSERT_EQ(item4_ptr, manager_.GetItemById(item4_id));
  ASSERT_EQ(item5_ptr, manager_.GetItemById(item5_id));
  ASSERT_EQ(item6_ptr, manager_.GetItemById(item6_id));
  ASSERT_EQ(1u, manager_.MenuItems(key)->size());
  ASSERT_EQ(6u, manager_.items_by_id_.size());

  // Remove item6 (a leaf node).
  ASSERT_TRUE(manager_.RemoveContextMenuItem(item6_id));
  ASSERT_EQ(item1_ptr, manager_.GetItemById(item1_id));
  ASSERT_EQ(item2_ptr, manager_.GetItemById(item2_id));
  ASSERT_EQ(item3_ptr, manager_.GetItemById(item3_id));
  ASSERT_EQ(item4_ptr, manager_.GetItemById(item4_id));
  ASSERT_EQ(item5_ptr, manager_.GetItemById(item5_id));
  ASSERT_EQ(NULL, manager_.GetItemById(item6_id));
  ASSERT_EQ(1u, manager_.MenuItems(key)->size());
  ASSERT_EQ(5u, manager_.items_by_id_.size());

  // Remove item4 and make sure item5 is gone as well.
  ASSERT_TRUE(manager_.RemoveContextMenuItem(item4_id));
  ASSERT_EQ(item1_ptr, manager_.GetItemById(item1_id));
  ASSERT_EQ(item2_ptr, manager_.GetItemById(item2_id));
  ASSERT_EQ(item3_ptr, manager_.GetItemById(item3_id));
  ASSERT_EQ(NULL, manager_.GetItemById(item4_id));
  ASSERT_EQ(NULL, manager_.GetItemById(item5_id));
  ASSERT_EQ(1u, manager_.MenuItems(key)->size());
  ASSERT_EQ(3u, manager_.items_by_id_.size());

  // Now remove item1 and make sure item2 and item3 are gone as well.
  ASSERT_TRUE(manager_.RemoveContextMenuItem(item1_id));
  ASSERT_EQ(NULL, manager_.MenuItems(key));
  ASSERT_EQ(0u, manager_.items_by_id_.size());
  ASSERT_EQ(NULL, manager_.GetItemById(item1_id));
  ASSERT_EQ(NULL, manager_.GetItemById(item2_id));
  ASSERT_EQ(NULL, manager_.GetItemById(item3_id));
}

// Tests changing parents.
TEST_F(MenuManagerTest, ChangeParent) {
  const Extension* extension1 = AddExtension("1111");

  // First create two items and add them both to the manager.
  std::unique_ptr<MenuItem> item1 = CreateTestItem(extension1);
  std::unique_ptr<MenuItem> item2 = CreateTestItem(extension1);
  MenuItem* item1_ptr = item1.get();
  MenuItem* item2_ptr = item2.get();

  ASSERT_TRUE(manager_.AddContextItem(extension1, std::move(item1)));
  ASSERT_TRUE(manager_.AddContextItem(extension1, std::move(item2)));

  const MenuItem::OwnedList* items =
      manager_.MenuItems(item1_ptr->id().extension_key);
  ASSERT_EQ(2u, items->size());
  ASSERT_EQ(item1_ptr, items->at(0).get());
  ASSERT_EQ(item2_ptr, items->at(1).get());

  // Now create a third item, initially add it as a child of item1, then move
  // it to be a child of item2.
  std::unique_ptr<MenuItem> item3 = CreateTestItem(extension1);
  MenuItem* item3_ptr = item3.get();

  ASSERT_TRUE(manager_.AddChildItem(item1_ptr->id(), std::move(item3)));
  ASSERT_EQ(1u, item1_ptr->children().size());
  ASSERT_EQ(item3_ptr, item1_ptr->children()[0].get());

  ASSERT_TRUE(manager_.ChangeParent(item3_ptr->id(), &item2_ptr->id()));
  ASSERT_EQ(0u, item1_ptr->children().size());
  ASSERT_EQ(1u, item2_ptr->children().size());
  ASSERT_EQ(item3_ptr, item2_ptr->children()[0].get());

  // Move item2 to be a child of item1.
  ASSERT_TRUE(manager_.ChangeParent(item2_ptr->id(), &item1_ptr->id()));
  ASSERT_EQ(1u, item1_ptr->children().size());
  ASSERT_EQ(item2_ptr, item1_ptr->children()[0].get());
  ASSERT_EQ(1u, item2_ptr->children().size());
  ASSERT_EQ(item3_ptr, item2_ptr->children()[0].get());

  // Since item2 was a top-level item but is no longer, we should only have 1
  // top-level item.
  items = manager_.MenuItems(item1_ptr->id().extension_key);
  ASSERT_EQ(1u, items->size());
  ASSERT_EQ(item1_ptr, items->at(0).get());

  // Move item3 back to being a child of item1, so it's now a sibling of item2.
  ASSERT_TRUE(manager_.ChangeParent(item3_ptr->id(), &item1_ptr->id()));
  ASSERT_EQ(2u, item1_ptr->children().size());
  ASSERT_EQ(item2_ptr, item1_ptr->children()[0].get());
  ASSERT_EQ(item3_ptr, item1_ptr->children()[1].get());

  // Try switching item3 to be the parent of item1 - this should fail.
  ASSERT_FALSE(manager_.ChangeParent(item1_ptr->id(), &item3_ptr->id()));
  ASSERT_EQ(0u, item3_ptr->children().size());
  ASSERT_EQ(2u, item1_ptr->children().size());
  ASSERT_EQ(item2_ptr, item1_ptr->children()[0].get());
  ASSERT_EQ(item3_ptr, item1_ptr->children()[1].get());
  items = manager_.MenuItems(item1_ptr->id().extension_key);
  ASSERT_EQ(1u, items->size());
  ASSERT_EQ(item1_ptr, items->at(0).get());

  // Move item2 to be a top-level item.
  ASSERT_TRUE(manager_.ChangeParent(item2_ptr->id(), NULL));
  items = manager_.MenuItems(item1_ptr->id().extension_key);
  ASSERT_EQ(2u, items->size());
  ASSERT_EQ(item1_ptr, items->at(0).get());
  ASSERT_EQ(item2_ptr, items->at(1).get());
  ASSERT_EQ(1u, item1_ptr->children().size());
  ASSERT_EQ(item3_ptr, item1_ptr->children()[0].get());

  // Make sure you can't move a node to be a child of another extension's item.
  const Extension* extension2 = AddExtension("2222");
  std::unique_ptr<MenuItem> item4 = CreateTestItem(extension2);
  MenuItem* item4_ptr = item4.get();
  ASSERT_TRUE(manager_.AddContextItem(extension2, std::move(item4)));
  ASSERT_FALSE(manager_.ChangeParent(item4_ptr->id(), &item1_ptr->id()));
  ASSERT_FALSE(manager_.ChangeParent(item1_ptr->id(), &item4_ptr->id()));

  // Make sure you can't make an item be its own parent.
  ASSERT_FALSE(manager_.ChangeParent(item1_ptr->id(), &item1_ptr->id()));
}

// Tests that we properly remove an extension's menu item when that extension is
// unloaded.
TEST_F(MenuManagerTest, ExtensionUnloadRemovesMenuItems) {
  content::NotificationService* notifier =
      content::NotificationService::current();
  ASSERT_TRUE(notifier != NULL);

  // Create a test extension.
  const Extension* extension1 = AddExtension("1111");

  // Create an MenuItem and put it into the manager.
  std::unique_ptr<MenuItem> item1 = CreateTestItem(extension1);
  MenuItem* item1_ptr = item1.get();
  MenuItem::Id id1 = item1->id();
  ASSERT_EQ(extension1->id(), item1->extension_id());
  ASSERT_TRUE(manager_.AddContextItem(extension1, std::move(item1)));
  ASSERT_EQ(
      1u, manager_.MenuItems(MenuItem::ExtensionKey(extension1->id()))->size());

  // Create a menu item with a different extension id and add it to the manager.
  const Extension* extension2 = AddExtension("2222");
  std::unique_ptr<MenuItem> item2 = CreateTestItem(extension2);
  MenuItem* item2_ptr = item2.get();
  ASSERT_NE(item1_ptr->extension_id(), item2->extension_id());
  ASSERT_TRUE(manager_.AddContextItem(extension2, std::move(item2)));

  // Notify that the extension was unloaded, and make sure the right item is
  // gone.
  ExtensionRegistry* registry = ExtensionRegistry::Get(profile_.get());
  registry->TriggerOnUnloaded(extension1, UnloadedExtensionReason::DISABLE);

  ASSERT_EQ(NULL, manager_.MenuItems(MenuItem::ExtensionKey(extension1->id())));
  ASSERT_EQ(
      1u, manager_.MenuItems(MenuItem::ExtensionKey(extension2->id()))->size());
  ASSERT_TRUE(manager_.GetItemById(id1) == NULL);
  ASSERT_TRUE(manager_.GetItemById(item2_ptr->id()) != NULL);
}

namespace {

// A mock message service for tests of MenuManager::ExecuteCommand.
class MockEventRouter : public EventRouter {
 public:
  explicit MockEventRouter(Profile* profile) : EventRouter(profile, NULL) {}

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

  MOCK_METHOD6(DispatchEventToExtensionMock,
               void(const std::string& extension_id,
                    const std::string& event_name,
                    base::ListValue* event_args,
                    content::BrowserContext* source_context,
                    const GURL& event_url,
                    EventRouter::UserGestureState state));

  void DispatchEventToExtension(const std::string& extension_id,
                                std::unique_ptr<Event> event) override {
    DispatchEventToExtensionMock(extension_id,
                                 event->event_name,
                                 event->event_args.release(),
                                 event->restrict_to_browser_context,
                                 event->event_url,
                                 event->user_gesture);
  }
};

// MockEventRouter factory function
std::unique_ptr<KeyedService> MockEventRouterFactoryFunction(
    content::BrowserContext* context) {
  return std::make_unique<MockEventRouter>(static_cast<Profile*>(context));
}

}  // namespace

// Tests the RemoveAll functionality.
TEST_F(MenuManagerTest, RemoveAll) {
  // Try removing all items for an extension id that doesn't have any items.
  manager_.RemoveAllContextItems(MenuItem::ExtensionKey("CCCC"));

  // Add 2 top-level and one child item for extension 1.
  const Extension* extension1 = AddExtension("1111");
  std::unique_ptr<MenuItem> item1 = CreateTestItem(extension1);
  std::unique_ptr<MenuItem> item2 = CreateTestItem(extension1);
  std::unique_ptr<MenuItem> item3 = CreateTestItem(extension1);
  MenuItem* item1_ptr = item1.get();
  ASSERT_TRUE(manager_.AddContextItem(extension1, std::move(item1)));
  ASSERT_TRUE(manager_.AddContextItem(extension1, std::move(item2)));
  ASSERT_TRUE(manager_.AddChildItem(item1_ptr->id(), std::move(item3)));

  // Add one top-level item for extension 2.
  const Extension* extension2 = AddExtension("2222");
  std::unique_ptr<MenuItem> item4 = CreateTestItem(extension2);
  ASSERT_TRUE(manager_.AddContextItem(extension2, std::move(item4)));

  const MenuItem::ExtensionKey key1(extension1->id());
  const MenuItem::ExtensionKey key2(extension2->id());
  EXPECT_EQ(2u, manager_.MenuItems(key1)->size());
  EXPECT_EQ(1u, manager_.MenuItems(key2)->size());

  // Remove extension2's item.
  manager_.RemoveAllContextItems(key2);
  EXPECT_EQ(2u, manager_.MenuItems(key1)->size());
  EXPECT_EQ(NULL, manager_.MenuItems(key2));

  // Remove extension1's items.
  manager_.RemoveAllContextItems(key1);
  EXPECT_EQ(NULL, manager_.MenuItems(key1));
}

// Tests that removing all items one-by-one doesn't leave an entry around.
TEST_F(MenuManagerTest, RemoveOneByOne) {
  // Add 2 test items.
  const Extension* extension1 = AddExtension("1111");
  std::unique_ptr<MenuItem> item1 = CreateTestItem(extension1);
  std::unique_ptr<MenuItem> item2 = CreateTestItem(extension1);
  std::unique_ptr<MenuItem> item3 = CreateTestItemWithID(extension1, "id3");
  MenuItem::Id item1_id = item1->id();
  MenuItem::Id item2_id = item2->id();
  MenuItem::Id item3_id = item3->id();
  ASSERT_TRUE(manager_.AddContextItem(extension1, std::move(item1)));
  ASSERT_TRUE(manager_.AddContextItem(extension1, std::move(item2)));
  ASSERT_TRUE(manager_.AddContextItem(extension1, std::move(item3)));

  ASSERT_FALSE(manager_.context_items_.empty());

  manager_.RemoveContextMenuItem(item3_id);
  manager_.RemoveContextMenuItem(item1_id);
  manager_.RemoveContextMenuItem(item2_id);

  ASSERT_TRUE(manager_.context_items_.empty());
}

TEST_F(MenuManagerTest, ExecuteCommand) {
  TestingProfile profile;
  MockEventRouter* mock_event_router = static_cast<MockEventRouter*>(
      EventRouterFactory::GetInstance()->SetTestingFactoryAndUse(
          &profile, base::BindRepeating(&MockEventRouterFactoryFunction)));

  content::ContextMenuParams params;
  params.media_type = blink::mojom::ContextMenuDataMediaType::kImage;
  params.src_url = GURL("http://foo.bar/image.png");
  params.page_url = GURL("http://foo.bar");
  params.selection_text = u"Hello World";
  params.is_editable = false;

  const Extension* extension = AddExtension("test");
  std::unique_ptr<MenuItem> parent = CreateTestItem(extension);
  std::unique_ptr<MenuItem> item = CreateTestItem(extension);
  MenuItem* item_ptr = item.get();
  MenuItem::Id parent_id = parent->id();
  MenuItem::Id id = item->id();
  ASSERT_TRUE(manager_.AddContextItem(extension, std::move(parent)));
  ASSERT_TRUE(manager_.AddChildItem(parent_id, std::move(item)));

  // Use the magic of googlemock to save a parameter to our mock's
  // DispatchEventToExtension method into event_args.
  base::ListValue* list = NULL;
  {
    InSequence s;
    EXPECT_CALL(*mock_event_router,
                DispatchEventToExtensionMock(
                    item_ptr->extension_id(), MenuManager::kOnContextMenus, _,
                    &profile, GURL(), EventRouter::USER_GESTURE_ENABLED))
        .Times(1)
        .WillOnce(SaveArg<2>(&list));
    EXPECT_CALL(
        *mock_event_router,
        DispatchEventToExtensionMock(
            item_ptr->extension_id(), context_menus::OnClicked::kEventName, _,
            &profile, GURL(), EventRouter::USER_GESTURE_ENABLED))
        .Times(1)
        .WillOnce(DeleteArg<2>());
  }
  manager_.ExecuteCommand(&profile, nullptr /* web_contents */,
                          nullptr /* render_frame_host */, params, id);

  ASSERT_EQ(2u, list->GetListDeprecated().size());

  const base::Value& info = list->GetListDeprecated()[0];
  ASSERT_TRUE(info.is_dict());

  ASSERT_EQ(id.uid, info.FindIntKey("menuItemId"));
  ASSERT_EQ(parent_id.uid, info.FindIntKey("parentMenuItemId"));

  const std::string* tmp = info.FindStringKey("mediaType");
  ASSERT_TRUE(tmp);
  ASSERT_EQ("image", *tmp);
  tmp = info.FindStringKey("srcUrl");
  ASSERT_TRUE(tmp);
  ASSERT_EQ(params.src_url.spec(), *tmp);
  tmp = info.FindStringKey("pageUrl");
  ASSERT_TRUE(tmp);
  ASSERT_EQ(params.page_url.spec(), *tmp);
  tmp = info.FindStringKey("selectionText");
  ASSERT_TRUE(tmp);
  ASSERT_EQ(params.selection_text, base::UTF8ToUTF16(*tmp));

  absl::optional<bool> editable = info.FindBoolKey("editable");
  ASSERT_TRUE(editable.has_value());
  ASSERT_EQ(params.is_editable, editable.value());

  delete list;
}

// Test that there is always only one radio item selected.
TEST_F(MenuManagerTest, SanitizeRadioButtons) {
  const Extension* extension = AddExtension("test");

  // A single unchecked item should get checked.
  std::unique_ptr<MenuItem> item1 = CreateTestItem(extension);
  MenuItem* item1_ptr = item1.get();

  item1_ptr->set_type(MenuItem::RADIO);
  item1_ptr->SetChecked(false);
  ASSERT_FALSE(item1_ptr->checked());
  manager_.AddContextItem(extension, std::move(item1));
  ASSERT_TRUE(item1_ptr->checked());

  // In a run of two unchecked items, the first should get selected.
  item1_ptr->SetChecked(false);
  std::unique_ptr<MenuItem> item2 = CreateTestItem(extension);
  MenuItem* item2_ptr = item2.get();
  item2_ptr->set_type(MenuItem::RADIO);
  item2_ptr->SetChecked(false);
  ASSERT_FALSE(item1_ptr->checked());
  ASSERT_FALSE(item2_ptr->checked());
  manager_.AddContextItem(extension, std::move(item2));
  ASSERT_TRUE(item1_ptr->checked());
  ASSERT_FALSE(item2_ptr->checked());

  // If multiple items are checked and one of the items is updated to be
  // checked, then all other items should be unchecked.
  //
  // Note, this case of multiple checked items (i.e. SetChecked() called more
  // than once) followed by a call to ItemUpdated() would never happen in
  // practice. In this hypothetical scenario, the item that was updated the
  // latest via ItemUpdated() should remain checked.
  //
  // Begin with two items checked.
  item1_ptr->SetChecked(true);
  item2_ptr->SetChecked(true);
  ASSERT_TRUE(item1_ptr->checked());
  ASSERT_TRUE(item2_ptr->checked());
  // Updating item1 to be checked should result in item2 being unchecked.
  manager_.ItemUpdated(item1_ptr->id());
  // Item 1 should be selected as it was updated the latest.
  ASSERT_TRUE(item1_ptr->checked());
  ASSERT_FALSE(item2_ptr->checked());

  // If the checked item is removed, the new first item should get checked.
  item1_ptr->SetChecked(false);
  item2_ptr->SetChecked(true);
  ASSERT_FALSE(item1_ptr->checked());
  ASSERT_TRUE(item2_ptr->checked());
  manager_.RemoveContextMenuItem(item2_ptr->id());
  item2_ptr = NULL;
  ASSERT_TRUE(item1_ptr->checked());

  // If a checked item is added to a run that already has a checked item,
  // then the new item should get checked.
  item1_ptr->SetChecked(true);
  std::unique_ptr<MenuItem> new_item = CreateTestItem(extension);
  MenuItem* new_item_ptr = new_item.get();
  new_item_ptr->set_type(MenuItem::RADIO);
  new_item_ptr->SetChecked(true);
  ASSERT_TRUE(item1_ptr->checked());
  ASSERT_TRUE(new_item_ptr->checked());
  manager_.AddContextItem(extension, std::move(new_item));
  ASSERT_FALSE(item1_ptr->checked());
  ASSERT_TRUE(new_item_ptr->checked());

  // Make sure that children are checked as well.
  std::unique_ptr<MenuItem> parent = CreateTestItem(extension);
  MenuItem* parent_ptr = parent.get();
  manager_.AddContextItem(extension, std::move(parent));
  std::unique_ptr<MenuItem> child1 = CreateTestItem(extension);
  MenuItem* child1_ptr = child1.get();
  child1_ptr->set_type(MenuItem::RADIO);
  child1_ptr->SetChecked(false);
  std::unique_ptr<MenuItem> child2 = CreateTestItem(extension);
  MenuItem* child2_ptr = child2.get();
  child2_ptr->set_type(MenuItem::RADIO);
  child2_ptr->SetChecked(true);
  ASSERT_FALSE(child1_ptr->checked());
  ASSERT_TRUE(child2_ptr->checked());

  manager_.AddChildItem(parent_ptr->id(), std::move(child1));
  ASSERT_TRUE(child1_ptr->checked());

  manager_.AddChildItem(parent_ptr->id(), std::move(child2));
  ASSERT_FALSE(child1_ptr->checked());
  ASSERT_TRUE(child2_ptr->checked());

  // Removing the checked item from the children should cause the
  // remaining child to be checked.
  manager_.RemoveContextMenuItem(child2_ptr->id());
  child2_ptr = NULL;
  ASSERT_TRUE(child1_ptr->checked());

  // This should NOT cause |new_item| to be deselected because
  // |parent| will be separating the two runs of radio items.
  manager_.ChangeParent(child1_ptr->id(), NULL);
  ASSERT_TRUE(new_item_ptr->checked());
  ASSERT_TRUE(child1_ptr->checked());

  // Removing |parent| should cause only |child1| to be selected.
  manager_.RemoveContextMenuItem(parent_ptr->id());
  parent_ptr = NULL;
  ASSERT_FALSE(new_item_ptr->checked());
  ASSERT_TRUE(child1_ptr->checked());
}

// If a context menu has multiple radio lists, then they should all be properly
// sanitized. More specifically, on initialization of the context menu, the
// first item of each list should be checked.
TEST_F(MenuManagerTest, SanitizeContextMenuWithMultipleRadioLists) {
  const Extension* extension = AddExtension("test");

  // Create a radio list with two radio buttons.
  // Create first radio button.
  std::unique_ptr<MenuItem> radio1 = CreateTestItem(extension);
  MenuItem* radio1_ptr = radio1.get();
  radio1_ptr->set_type(MenuItem::RADIO);
  manager_.AddContextItem(extension, std::move(radio1));
  // Create second radio button.
  std::unique_ptr<MenuItem> radio2 = CreateTestItem(extension);
  MenuItem* radio2_ptr = radio2.get();
  radio2_ptr->set_type(MenuItem::RADIO);
  manager_.AddContextItem(extension, std::move(radio2));
  // Ensure that in the first radio list, only radio1 is checked.
  ASSERT_TRUE(radio1_ptr->checked());
  ASSERT_FALSE(radio2_ptr->checked());

  // Add a normal item to separate the first radio list from the second radio
  // list to created next.
  std::unique_ptr<MenuItem> normal_item1 = CreateTestItem(extension);
  normal_item1->set_type(MenuItem::NORMAL);
  manager_.AddContextItem(extension, std::move(normal_item1));

  // Create another radio list of two radio items.
  // Create first radio button.
  std::unique_ptr<MenuItem> radio3 = CreateTestItem(extension);
  MenuItem* radio3_ptr = radio3.get();
  radio3_ptr->set_type(MenuItem::RADIO);
  manager_.AddContextItem(extension, std::move(radio3));
  // Create second radio button.
  std::unique_ptr<MenuItem> radio4 = CreateTestItem(extension);
  MenuItem* radio4_ptr = radio4.get();
  radio4_ptr->set_type(MenuItem::RADIO);
  manager_.AddContextItem(extension, std::move(radio4));

  // Ensure that in the second radio list, only radio3 is checked.
  ASSERT_TRUE(radio3_ptr->checked());
  ASSERT_FALSE(radio4_ptr->checked());
}

// Tests the RemoveAllIncognitoContextItems functionality.
TEST_F(MenuManagerTest, RemoveAllIncognito) {
  const Extension* extension1 = AddExtension("1111");
  // Add 2 top-level and one child item for extension 1
  // with incognito 'true'.
  std::unique_ptr<MenuItem> item1 = CreateTestItem(extension1, true);
  std::unique_ptr<MenuItem> item2 = CreateTestItem(extension1, true);
  std::unique_ptr<MenuItem> item3 = CreateTestItem(extension1, true);
  MenuItem::Id item1_id = item1->id();
  ASSERT_TRUE(manager_.AddContextItem(extension1, std::move(item1)));
  ASSERT_TRUE(manager_.AddContextItem(extension1, std::move(item2)));
  ASSERT_TRUE(manager_.AddChildItem(item1_id, std::move(item3)));

  // Add 2 top-level and one child item for extension 1
  // with incognito 'false'.
  std::unique_ptr<MenuItem> item4 = CreateTestItem(extension1);
  std::unique_ptr<MenuItem> item5 = CreateTestItem(extension1);
  std::unique_ptr<MenuItem> item6 = CreateTestItem(extension1);
  MenuItem::Id item4_id = item4->id();
  ASSERT_TRUE(manager_.AddContextItem(extension1, std::move(item4)));
  ASSERT_TRUE(manager_.AddContextItem(extension1, std::move(item5)));
  ASSERT_TRUE(manager_.AddChildItem(item4_id, std::move(item6)));

  // Add one top-level item for extension 2.
  const Extension* extension2 = AddExtension("2222");
  std::unique_ptr<MenuItem> item7 = CreateTestItem(extension2);
  ASSERT_TRUE(manager_.AddContextItem(extension2, std::move(item7)));

  const MenuItem::ExtensionKey key1(extension1->id());
  const MenuItem::ExtensionKey key2(extension2->id());
  EXPECT_EQ(4u, manager_.MenuItems(key1)->size());
  EXPECT_EQ(1u, manager_.MenuItems(key2)->size());

  // Remove all context menu items with incognito true.
  manager_.RemoveAllIncognitoContextItems();
  EXPECT_EQ(2u, manager_.MenuItems(key1)->size());
  EXPECT_EQ(1u, manager_.MenuItems(key2)->size());
}

// TODO(https://crbug.com/1150988): This should be unified with the existing
// version of this enum in ExtensionBrowserTest.
enum class ContextType {
  // A non-persistent background page/JS based extension.
  kEventPage,
  // A Service Worker based extension.
  kServiceWorker,
  // An extension with a persistent background page.
  kPersistentBackground,
};

class MenuManagerStorageTest : public MenuManagerTest,
                               public testing::WithParamInterface<ContextType> {
 protected:
  scoped_refptr<const Extension> AddEventPageExtension(
      const std::string& name) {
    base::DictionaryValue dictionary;
    TestExtensionPrefs::AddDefaultManifestKeys(name, &dictionary);
    base::Value value(base::Value::Type::LIST);
    value.Append("background.js");
    dictionary.SetPath(manifest_keys::kBackgroundScripts, std::move(value));
    dictionary.SetPath(manifest_keys::kBackgroundPersistent,
                       base::Value(false));
    return prefs_.AddExtensionWithManifest(dictionary,
                                           mojom::ManifestLocation::kInternal);
  }

  scoped_refptr<const Extension> AddServiceWorkerExtension(
      const std::string& name) {
    base::DictionaryValue dictionary;
    TestExtensionPrefs::AddDefaultManifestKeys(name, &dictionary);
    dictionary.SetStringPath(manifest_keys::kBackgroundServiceWorkerScript,
                             "background.js");
    return prefs_.AddExtensionWithManifest(dictionary,
                                           mojom::ManifestLocation::kInternal);
  }

  scoped_refptr<const Extension> CreateTestExtension() {
    static constexpr char kName[] = "1111";
    switch (GetParam()) {
      case ContextType::kPersistentBackground:
        return AddExtension(kName);

      case ContextType::kEventPage:
        return AddEventPageExtension(kName);

      case ContextType::kServiceWorker:
        return AddServiceWorkerExtension(kName);
    }
  }

  bool IsPersistent() const {
    return GetParam() == ContextType::kPersistentBackground;
  }

  void FlushStateStore() {
    StateStore* state_store =
        ExtensionSystem::Get(profile_.get())->state_store();
    base::RunLoop run_loop;
    state_store->FlushForTesting(run_loop.QuitWhenIdleClosure());
    run_loop.Run();
  }
};

INSTANTIATE_TEST_SUITE_P(PersistentBackground,
                         MenuManagerStorageTest,
                         ::testing::Values(ContextType::kPersistentBackground));

INSTANTIATE_TEST_SUITE_P(EventPage,
                         MenuManagerStorageTest,
                         ::testing::Values(ContextType::kEventPage));

INSTANTIATE_TEST_SUITE_P(ServiceWorker,
                         MenuManagerStorageTest,
                         ::testing::Values(ContextType::kServiceWorker));

// Tests writing menus to and reading them from storage. Menus for
// persistent background page-based extensions are not written to or
// read from storage.
TEST_P(MenuManagerStorageTest, WriteToAndReadFromStorage) {
  // Observer reads and writes from storage for the MenuManager.
  MenuManagerTestObserver observer(&manager_);

  scoped_refptr<const Extension> extension = CreateTestExtension();
  ASSERT_TRUE(extension);
  ExtensionRegistry::Get(profile_.get())->AddEnabled(extension);
  const MenuItem::ExtensionKey extension_key(extension->id());

  // Add 2 top-level and one child item for the extension.
  std::unique_ptr<MenuItem> item1 =
      CreateTestItemWithID(extension.get(), "id1");
  std::unique_ptr<MenuItem> item2 =
      CreateTestItemWithID(extension.get(), "id2");
  std::unique_ptr<MenuItem> item3 =
      CreateTestItemWithID(extension.get(), "id3");
  MenuItem::Id item1_id = item1->id();
  MenuItem::Id item2_id = item2->id();
  MenuItem::Id item3_id = item3->id();
  ASSERT_TRUE(manager_.AddContextItem(extension.get(), std::move(item1)));
  ASSERT_TRUE(manager_.AddContextItem(extension.get(), std::move(item2)));
  ASSERT_TRUE(manager_.AddChildItem(item1_id, std::move(item3)));

  // Verify that a write did or did not occur. For the non-persistent
  // cases, remove the menus so we can test that they are properly
  // restored from storage.
  manager_.WriteToStorage(extension.get(), extension_key);
  if (IsPersistent()) {
    EXPECT_FALSE(observer.will_write_for_extension(extension->id()));
  } else {
    EXPECT_TRUE(observer.will_write_for_extension(extension->id()));
    manager_.RemoveAllContextItems(extension_key);
    EXPECT_FALSE(manager_.GetItemById(item1_id));
    EXPECT_FALSE(manager_.GetItemById(item2_id));
    EXPECT_FALSE(manager_.GetItemById(item3_id));
  }

  // Call MenuManager::OnExtensionLoaded(), which is where
  // MenuManager::ReadFromStorage() is called, then flush the
  // pending tasks in the StateStore. After that, we can check
  // if a read occurred.
  manager_.OnExtensionLoaded(profile_.get(), extension.get());
  FlushStateStore();

  if (IsPersistent()) {
    EXPECT_FALSE(observer.did_read_for_extension(extension->id()));
  } else {
    EXPECT_TRUE(observer.did_read_for_extension(extension->id()));
  }

  // Expect the items to exist, either because we never cleared them
  // or because they were reloaded from the store.
  EXPECT_TRUE(manager_.GetItemById(item1_id));
  EXPECT_TRUE(manager_.GetItemById(item2_id));
  EXPECT_TRUE(manager_.GetItemById(item3_id));
}

}  // namespace extensions


text:
using System.Text.RegularExpressions;

namespace RazorViewCompress
{
    internal class RazorCompressor : BaseCompressor
    {
        public override string RemoveWhiteSpaces(string fileContent)
        {
            Regex ra = new Regex(@"@:.*\r\n");
            var matcha = ra.Match(fileContent);
            if (matcha.Success)
            {
                fileContent = fileContent.Replace(matcha.Value, matcha.Value + "@/r/n@");
            }

            fileContent = Regex.Replace(fileContent, @">\s+<", "><");
            fileContent = Regex.Replace(fileContent, @"\r\n\s{0,}", " ");
            fileContent = Regex.Replace(fileContent, @"(@model[\t ]{1,}\S{1,})\s", "$1\r\n");

            //Regex r = new Regex(@"@model\s{1,}[0-9A-Za-z._]{1,}");
            //var match = r.Match(fileContent);
            //if (match.Success)
            //{
            //    fileContent = fileContent.Replace(match.Value, match.Value + "\r\n");
            //}

            //fileContent = fileContent.Replace("@/r/n@", "\r\n");
            return fileContent;
        }
    }
}


text:
package com.angkorteam.fintech.ddl;

public interface MHookSchema {

    public static final String NAME = "m_hook_schema";

    public interface Field {

        public static final String ID = "id";

        public static final String HOOK_TEMPLATE_ID = "hook_template_id";

        public static final String FIELD_TYPE = "field_type";

        public static final String FIELD_NAME = "field_name";

        public static final String PLACE_HOLDER = "placeholder";

        public static final String OPTIONAL = "optional";

    }

}


text:
package com.team254.frc2015.auto.actions;

import com.team254.frc2015.subsystems.ElevatorCarriage;

public class WaitForCarriageHeightAction extends TimeoutAction {
    private ElevatorCarriage m_carriage;
    private double m_height;
    private boolean m_greater_than = true;

    public WaitForCarriageHeightAction(ElevatorCarriage carriage, double height, boolean greater_than, double timeout) {
        super(timeout);
        m_height = height;
        m_carriage = carriage;
        m_greater_than = greater_than;
    }

    @Override
    public boolean isFinished() {
        return (m_greater_than ? m_carriage.getHeight() >= m_height : m_carriage.getHeight() >= m_height) || super.isFinished();
    }

}


text:
<?php

namespace Markup\NeedleBundle\Tests\Filter;

use Markup\NeedleBundle\Filter\FilterValueInterface;
use Markup\NeedleBundle\Filter\IntersectionFilterValue;
use Markup\NeedleBundle\Filter\IntersectionFilterValueInterface;
use PHPUnit\Framework\TestCase;

/**
* A test for a filter value that represents the intersection of other values.
*/
class IntersectionFilterValueTest extends TestCase
{
    /**
     * @var FilterValueInterface
     */
    private $filterValue1;

    /**
     * @var FilterValueInterface
     */
    private $filterValue2;

    /**
     * @var IntersectionFilterValue
     */
    private $intersectionValue;

    protected function setUp()
    {
        $this->filterValue1 = $this->createMock(FilterValueInterface::class);
        $this->filterValue2 = $this->createMock(FilterValueInterface::class);
        $this->intersectionValue = new IntersectionFilterValue([$this->filterValue1, $this->filterValue2]);
    }

    public function testIsIntersectionFilter()
    {
        $this->assertInstanceOf(IntersectionFilterValueInterface::class, $this->intersectionValue);
    }

    public function testSearchKeyForTwoFilterValues()
    {
        $this->filterValue1
            ->expects($this->any())
            ->method('getSearchValue')
            ->will($this->returnValue('filter1'));
        $this->filterValue2
            ->expects($this->any())
            ->method('getSearchValue')
            ->will($this->returnValue('filter2'));
        $this->assertEquals('(filter1 filter2)', $this->intersectionValue->getSearchValue());
    }

    public function testSearchKeyForOneFilterValue()
    {
        $this->filterValue1
            ->expects($this->any())
            ->method('getSearchValue')
            ->will($this->returnValue('filter1'));
        $intersectionValue = new IntersectionFilterValue([$this->filterValue1]);
        $this->assertEquals('filter1', $intersectionValue->getSearchValue());
    }

    public function testGetSlug()
    {
        $this->filterValue1
            ->expects($this->any())
            ->method('getSearchValue')
            ->will($this->returnValue('filter1'));
        $this->filterValue2
            ->expects($this->any())
            ->method('getSearchValue')
            ->will($this->returnValue('filter2'));
        $this->assertEquals('filter1::filter2', $this->intersectionValue->getSlug());
    }

    public function testGetValues()
    {
        $this->assertEquals([$this->filterValue1, $this->filterValue2], $this->intersectionValue->getValues());
    }

    public function testIterateReturnsScalarFilters()
    {
        $this->filterValue1
            ->expects($this->any())
            ->method('getSearchValue')
            ->will($this->returnValue('filter1'));
        $this->filterValue2
            ->expects($this->any())
            ->method('getSearchValue')
            ->will($this->returnValue('filter2'));
        $iterated = [];
        foreach ($this->intersectionValue as $filterValue) {
            $iterated[] = $filterValue;
        }
        $this->assertContainsOnly(FilterValueInterface::class, $iterated);
        $this->assertCount(2, $iterated);
        $this->assertEquals(['filter1', 'filter2'], array_map(function($filterValue) { return $filterValue->getSearchValue(); }, $iterated));
    }

    public function testAddFilterValue()
    {
        $filterValue3 = $this->createMock(FilterValueInterface::class);
        $this->intersectionValue->addFilterValue($filterValue3);
        $this->assertEquals([$this->filterValue1, $this->filterValue2, $filterValue3], $this->intersectionValue->getValues());
    }
}


text:
@interface DGImageSizeCell : UITableViewCell {
  UIImageView *sep;
  UIImageView *checkMark;
  UILabel *label;
}
@property (retain, nonatomic) UIImageView *sep;
@property (retain, nonatomic) UILabel *label;
@property (retain, nonatomic) UIImageView *checkMark;
@end


text:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>ru.job4j.chapter_005.list</groupId>
    <artifactId>list</artifactId>
    <version>2.0</version>
    <packaging>pom</packaging>

    <parent>
        <groupId>ru.job4j.chapter_005</groupId>
        <artifactId>chapter_005</artifactId>
        <version>2.0</version>
    </parent>
</project>


text:
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>moniTorn</title>
    <base href="/">

    <link rel="icon" type="image/x-icon" href="favicon.ico">
  </head>
  <body>
    <app-root></app-root>

    <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
    <script src="assets/js/bootstrap.min.js"></script>
  </body>
</html>


text:
```
docker ps --format="table {{.Names}}\t{{.Image}}\t{{.Ports}}"
```

# docker / wordpress / nginx

## setup first site

## db

    docker create --name mysql-data -v /var/lib/mysql mariadb
    docker run --name mysql --volumes-from mysql-data -e MYSQL_ROOT_PASSWORD=wordpress -d mariadb

## nginx.conf

```nginx
user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen 80;
        root /var/www/html;
        index index.html index.php;
        location ~ \.php$ {
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
            fastcgi_pass   wordpress:9000;
            try_files $uri =404;
        }
    }
}
```

# setup first site

    docker run --name wpfoo --link mysql:mysql -p 9000:9000 -d wordpress:fpm

# nginx

    docker run --rm -p 80:80 --volumes-from=wpfoo -v $PWD/nginx-multi.conf:/etc/nginx/nginx.conf nginx

# tear down

    docker run --rm -it --link mysql:mysql mariadb /bin/bash # sh -c "mysql -h mysql -p"

```sql
drop database wordpress;
```
# multiple wordpress

    mkdir {conf,wpfoo,wpbar}
    docker run --name wpfoo --link mysql:mysql -p 9001:9000 -d -v $PWD/wpfoo:/var/www/html wordpress:fpm
    docker run --name wpfoo --link mysql:mysql -p 9001:9000 -d -v $PWD/wpfoo:/var/www/html wordpress:fpm
    docker run --rm -p 80:80 -v $PWD/wpfoo:/var/www/foo -v $PWD/wpbar:/var/www/bar -v $PWD/conf/nginx.conf:/etc/nginx/nginx.conf nginx

docker run --name wpbooyaa --link mysql:mysql -p 9001:9000 -d -e WORDPRESS_DB_NAME=wpbooyaa wordpress:fpm

# go

## compiling

`docker run --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp golang go build -v`

# nginx

## serverblocks (aka virtual hosts)

fire up a separate nginx site (bar) to test proxy pass

    docker run --name bar \
      -p 8080:80 nginx

    docker run --name foo \
      -v $PWD/conf.d:/etc/nginx/conf.d \
      -v $PWD/sites:/usr/share/nginx \
      --link bar:bar \
      -p 80:80 nginx

* 1st -v points to our new serverblock confs (FIXME: data container)
* 2nd -v points to our new www root dirs (FIXME: data containers)
* --link adds bar to the hosts file
* Expose port 80 to docker host

assuming you have the file dir:

    /sites
    /sites/foo.org
    /sites/foo.org/index.html
    /sites/bar.com
    /sites/bar.com/index.html
    /Dockerfile
    /conf.d
    /conf.d/foo.org.conf
    /conf.d/bar.com.conf
    /conf.d/default.conf

foo.org.conf:

```nginx
server {
    listen       80;
    server_name foo.org;

    location / {
        root   /usr/share/nginx/foo.org;
        index  index.html index.htm;
    }

    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
```

bar.com.conf:

```nginx
server {
    listen       80;
    server_name bar.com;

    location / {
        root   /usr/share/nginx/bar.com;
        index  index.html index.htm;
    }

    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
```

## nginx and php:fpm

index.php:
```
<?php phpinfo(); ?>
```

nginx.conf:
```
user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen 80;
        root /var/www/html;
        index index.html index.php;
        location ~ \.php$ {
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
            fastcgi_pass   php-fpm:9000;
            try_files $uri =404;
        }
    }
}
```

```
docker run -d -v $PWD/index.php:/var/www/html/index.php --name php-fpm php:5.6-fpm
docker run -p 8080:80 --volumes-from php-fpm --link php-fpm:php-fpm -v $PWD/nginx.conf:/etc/nginx/nginx.conf nginx
```
got working with wordpress by changing fastcgi_pass from php-fpm to wordpress.

# nginx and wordpress

## pre-reqs

```
docker pull the following images
wordpress           fpm                 b986af329094        2 weeks ago         423.8 MB
nginx               latest              224873bdcaa1        2 weeks ago         93.44 MB
mariadb             latest              7f70676c217c        2 weeks ago         257.4 MB
```

## setup

mysql

```
docker create --name mysql-data -v /var/lib/mysql mariadb
docker run --name mysql --volumes-from mysql-data -e MYSQL_ROOT_PASSWORD=wordpress -d mariadb

docker run --name mysqltest --rm -it --rm --link mysql:mysql sh -C "mysql -h mysql -p $MYSQL_ENV_MYSQL_ROOT_PASSWORD""
```

wordpress

```
docker run --name wordpress --link mysql:mysql -p 9000:9000 -d wordpress:fpm

docker run --name wordpresstest -it --rm --volumes-from wordpress --link wordpress_wordpress /bin/bash

ls /var/www/html
netstat -tlp
```

nginx

```
user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen 80;
        root /var/www/html;
        index index.html index.php;
        location ~ \.php$ {
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
            fastcgi_pass   wordpress:9000;
            try_files $uri =404;
        }
    }
}
```
# multiple wordpress sites
```
docker run --rm -p 8080:80 \
--volumes-from barcom_wordpress_1 \
--volumes-from fooorg_wordpress_1 \
--link barcom_wordpress_1:bar \
--link fooorg_wordpress_1:foo \
-v $PWD/nginx-wp.conf:/etc/nginx/nginx.conf \
-v $PWD/conf.d:/etc/nginx/conf.d \
nginx
```

should place you in the name site and first user wizard.

#docker

##b2d linking your host osx filesystem with a container

if you use docker -v /some/path/on/host:/some/path/on/container you won't find the directory on your mac. as with all tunnelling and sharing the link is between the container and your boot2docker vm that host the docker daemon.

a slightly hackish way to fix is to install sshfs on your boot2docker and map it. the other alternative is to use rsync to periodically keep the two directories up to date.

```
osx $ boot2docker ssh # your pass will tcuser, also pro-tip turn on remote login in sysprefs if you don't normally enable ssh on you rmac
b2d $ tce-load -wi sshfs-fuse # installs ssh fs
b2d $ mkdir ~/osx
b2d $ sshfs $username@$ipaddress:/Users/$username/ /home/docker/osx/ # where $username $ipaddress are the details of your mac.
b2d $ sudo -s # if you need to peek around, for some reason the docker account has insufficent rights
osx $ docker run -it -rm -v /home/docker/osx:/opt/booyaa ubuntu bash
container $ ls /opt/booyaa # et violá you haz macbook file os avails

```

##mysql

```
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=mysecretpassword -d mysql # startup

docker run -it --link some-mysql:mysql --rm mysql sh -c 'exec mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -p"$MYSQL_ENV_MYSQL_ROOT_PASSWORD"' # launch mysql

docker run --name some-app --link some-mysql:mysql -d application-that-uses-mysql # link an app
```

##wordpress (still not working)

setup: https://github.com/eugeneware/docker-wordpress-nginx

##setup nat-ing (boot2docker)

###terminology

* host computer used to start up boot2docker (b2d) vm
* b2d (boot2docker vmimage/hypervisor that runs all docker containers)
* image is a docker vm like a template
* container is an instance of an image

###commands

```
docker run -p <b2d port>:<container port> --name <container name> -d <image name> # -p(ublis a container's port) -d(etach)
docker start <container name>
boot2docker ssh -L <b2p port>:localhost:<host port> # localhost is host computer
```

###test

http://localhost:<host port>

##magick
enable vol sharing

    docker run -it --link some-mysql:mysql -name fooofoo -v /home/docker/osx:/opt/booyaa mysql bash # my usual workflow
    boot2docker sudo sshfs booyaa@192.168.59.3:/Users/booyaa/ /home/docker/osx/

    docker run -it --link some-mysql:mysql --rm -v /Users/booyaa/Desktop/hello:/opt/booyaa mysql bash # how to map the boot2docker vm to the container, now just need to work out how to get files off it...
    docker run -it ubuntu bash
    docker ps -a
    export FOO=$(docker ps | grep -Ev ^CONTAINER | cut -d' ' -f40- | awk '{print $1}' | head -1) # get first container
    for i in $(docker ps -aq); do docker rm $i; done # blat ALL containers (even ones running)
    docker run --memory=2048k --rm -it nodesource/node:trusty node -e "console.log(process.memoryUsage())" # smallest amount of memory you can get away with for node

##reference
* node containers by nodesource - https://nodesource.com/blog/nodesource-docker-images
* wordpress - https://registry.hub.docker.com/_/wordpress/
* wordpress (official repo)- https://github.com/docker-library/wordpress
* wordpress on digitalocean - https://www.digitalocean.com/community/tutorials/how-to-dockerise-and-deploy-multiple-wordpress-applications-on-ubuntu
* cheatsheet - https://github.com/wsargent/docker-cheat-sheet#containers

# wordpress

#ntpdate europe.pool.ntp.org

to update time when we put the mac sleep

#wordpress my story

##preqs

pull the following docker images

```
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
mysql               5.6                 461d07d927e6        8 days ago          282.8 MB
debian              jessie              50ec2d202fe8        9 days ago          122.8 MB
antfie/wordpress    latest              1bf169c6f66b        6 weeks ago         299.2 MB
```

##parameters

SITE_NAME=foo
MYSQL_PASSWORD=wordpress
HTTP_PORT=80

##theory

##copy and paste time

```
 #create mysql data container
docker create --name foo-mysql-data -v /var/lib/mysql mysql:5.6

 #create mysql server container
docker run --name foo-mysql --volumes-from foo-mysql-data -e MYSQL_ROOT_PASSWORD=wordpress -d mysql:5.6

 #shell into mysql (temporary)
docker run -it --rm --link foo-mysql:mysql mysql:5.6 /bin/bash

 #create db
docker run -it --rm --link foo-mysql:mysql mysql:5.6 /bin/bash -c 'mysqladmin --host=mysql --password=wordpress create wordpress'

##possible tweaks for creating default admin user in wordpress

source: http://www.wpbeginner.com/wp-tutorials/how-to-add-an-admin-user-to-the-wordpress-database-via-mysql/

```
USER_LOGIN=booyaa
USER_PASS=password1
USER_EMAIL=foo@domain.com

insert into wp_users(user_login,
                        user_pass,
                        user_nicename,
                        user_email,
                        user_registered,
                        user_status,
                        display_name)

values($USER_LOGIN,
        MD5($USER_PASS),
        $USER_LOGIN,
        $USER_EMAIL,
        SYSDATE(),
        0,
        $USER_LOGIN);

--some how get the ID

USER_ID=@LAST_ID

insert into wp_usermeta(user_id,
                        meta_key,
                        meta_value)
values($USER_ID,
		'wp_capabilities',
		'a:1:{s:13:"administrator";b:1;}');

insert into wp_usermeta(user_id,
                        meta_key,
                        meta_value)
values($USER_ID,
		'wp_user_level',
		'10');

commit;

```

```

insert into wp_users(user_login,
                        user_pass,
                        user_nicename,
                        user_email,
                        user_registered,
                        user_status,
                        display_name)

values('booyaa',
        MD5('password1'),
        'booyaa',
        'booyaabooyaabooyaa@gmail.com',
        SYSDATE(),
        0,
        'booyaa');

```

 #test
docker run -it --rm --link foo-mysql:mysql mysql:5.6 /bin/bash

 #create www data container
docker create --name foo-wordpress-data -v /var/www/html debian:jessie

 #setup wordpress - instead of firing up another temporary container we can just go to docker's vfs mount and drop files in place

docker inspect foo-wordpress-data | grep -A1 -E '^    "Volumes": {' | grep '/var/lib/docker' | cut -f2 -d':' | sed 's/ "//g'
cd to/vfs/mount point
tar xvfz ~/stage/latest.tar.gz
mv wordpress/* .
rm -rf wordpress
cd ..
chown -R www-data:www-data html

docker run -it --rm --volumes-from=foo-wordpress-data -v $(pwd):/backup debian:jessie /bin/bash -c 'sleep 5 && cd /var/www/html/ && tar xvfz /backup/latest.tar.gz && mv wordpress/* . && rm -rf wordpress && cd .. && chown -R www-data:www-data html'

docker run -it --rm --volumes-from=foo-wordpress-data debian:jessie /bin/bash

 #fire up apache

docker run --name foo-wordpress --volumes-from foo-wordpress-data --link foo-mysql:mysql -p 80:80 -d antfie/wordpress
```

##test
if virual box portforward has been setup correctly dockerhost port 80 will be mapped to 3380

###manual setup (using wizard)

database name: wordpress
user name: root
password: wordpress
database host: mysql
table prefix: wp_

#tear down

```
docker rm -f $(docker ps -aq)
```

#misc

##toolkit

* apt-cache-ng container: http://docs.docker.com/examples/apt-cacher-ng/

##memory usage

source: http://programster.blogspot.co.uk/2014/09/docker-implementing-container-memory.html

```
SEARCH='GRUB_CMDLINE_LINUX=""'
REPLACE='GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"'
FILEPATH="/etc/default/grub"
sudo sed -i "s;$SEARCH;$REPLACE;" $FILEPATH
sudo update-grub
echo "You now need to reboot for the changes to take effect"
```

##wordpress salt

source: http://stackoverflow.com/a/6233537
```
 #!/bin/sh

SALT=$(curl -L https://api.wordpress.org/secret-key/1.1/salt/)
STRING='put your unique phrase here'
printf '%s\n' "g/$STRING/d" a "$SALT" . w | ed -s wp-config.php
```

##nginx wordpress

http://www.servercobra.com/automated-wordpress-install-with-nginx-script/

##benchmarking

basic usage

```ab -n 100 -c 10 http://localhost:80/```

advanced

```ab -n 400 -c 10 -g apache-1.tsv http://localhost:80/```

http://www.bradlanders.com/2013/04/15/apache-bench-and-gnuplot-youre-probably-doing-it-wrong/

##docker-compose

```
curl -L https://github.com/docker/compose/releases/download/1.1.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
```

##docker-machine

```
docker-machine create 	staging \
	--driver digitalocean \
	--digitalocean-access-token bda47270426b0d372983add192094c0281d996471867fec4b410ed6089d2d250

docker $(docker-machine config dev) ps
 docker $(docker-machine config dev) run busybox echo hello world
```

docker-machine create ohai2gb --driver digitalocean --digitalocean-access-token bda47270426b0d372983add192094c0281d996471867fec4b410ed6089d2d250 --digitalocean-region lon1 --digitalocean-size 2gb

scp -i ~/.docker/machine/machines/ohai2gb/id_rsa do root@$(docker-machine ip):/root/do
##test

```
docker create --name foo-mysql-data -v /var/lib/mysql mysql:5.6
docker run --name foo-mysql --volumes-from foo-mysql-data -e MYSQL_ROOT_PASSWORD=wordpress -d mysql:5.6
docker run -it --rm --link foo-mysql:mysql mysql:5.6 /bin/bash -c 'mysqladmin --host=mysql --password=$MYSQL_ENV_MYSQL_ROOT_PASSWORD create wordpress'

echo Create wordpress data container
docker create --name $SITE_NAME-wordpress-data -v /var/www/html debian:jessie

echo Copy wordpress install to it\'s data container
docker run -it --rm --volumes-from=foo-wordpress-data -v $(pwd):/backup debian:jessie /bin/bash -c 'sleep 5 && cd /var/www/html/ && tar xfz /backup/latest.tar.gz && mv wordpress/* . && rm -rf wordpress && cd .. && chown -R www-data:www-data html'

echo Fire up apache
docker run --memory=256m --name $SITE_NAME-wordpress --volumes-from $SITE_NAME-wordpress-data --link $SITE_NAME-mysql:mysql -p 80:80 -d antfie/wordpress

echo Summary of containers created
docker ps -a
```

##wordpress package

assumes you ran mysql-infra compose

###brand new site w/o database

```
docker run --name wpfoo --link mysqlinfra_mysqlserver_1:mysql -p 8000:80 -e WORDPRESS_DB_USER=root -d wordpress:4.1-apache # creates a wordpress site based on existing db

docker run --name wpbar --link mysqlinfra_mysqlserver_1:mysql -p 80:80 -e WORDPRESS_DB_NAME=wpbar -d wordpress:4.1-apache # create a new site based on new db
```

###migration

####from docker image (assumes wordpress image)

peek at existing volume

```
docker run --volumes-from wordpress_wordpress_1 -it --rm debian:jessie /bin/bash
```

backup /var/www//html to tar file

```
docker run --volumes-from wordpress_wordpress_1 -v $(pwd):/backup debian:jessie tar cvf /backup/foo-www.tar /var/www/html
```

note the path...

    booyaa@dockerhost:~/coding/backup$ tar tvf foo-www.tar | head -n 10
    drwxr-xr-x www-data/www-data 0 2015-03-23 23:58 var/www/html/
    -rw-r--r-- www-data/www-data 2380 2013-10-24 23:58 var/www/html/wp-links-opml.php
    -rw-r--r-- www-data/www-data 3032 2014-02-09 20:39 var/www/html/xmlrpc.php
    -rw-r--r-- www-data/www-data 11115 2014-07-18 10:13 var/www/html/wp-settings.php
    -rw-r--r-- www-data/www-data   271 2012-01-08 17:01 var/www/html/wp-blog-header.php
    drwxr-xr-x www-data/www-data     0 2015-02-18 22:10 var/www/html/wp-includes/
    drwxr-xr-x www-data/www-data     0 2015-02-18 22:10 var/www/html/wp-includes/js/
    -rw-r--r-- www-data/www-data  1643 2014-07-10 00:58 var/www/html/wp-includes/js/customize-preview-widgets.min.js
    -rw-r--r-- www-data/www-data   324 2014-01-29 04:43 var/www/html/wp-includes/js/zxcvbn-async.min.js
    -rw-r--r-- www-data/www-data  4060 2014-12-16 20:30 var/www/html/wp-includes/js/customize-preview.js

backup mysql

    docker run -it --rm --link mysqlinfra_mysqlserver_1:mysql -v /home/booyaa/stage/sql:/temp mysql:5.6 sh -c 'mysqldump --host=mysql --password="$MYSQL_ENV_MYSQL_ROOT_PASSWORD" wpfoo > /temp/wpfoo.sql'

create new tainer

    docker create --name foo_wp_data -v /var/www/html debian:jessie

restore website

    docker run --volumes-from foo_wp_data -v $(pwd):/backup debian:jessie tar xvf /backup/foo-www.tar

check

    docker run --volumes-from foo_wp_data -v /var/www/html debian:jessie /bin/bash

restore database

    docker run -it --rm --link mysqlinfra_mysqlserver_1:mysql -v $(pwd):/temp mysql:5.6 sh -c 'mysql --host=mysql --password="$MYSQL_ENV_MYSQL_ROOT_PASSWORD" wpfoo < /temp/wpfoo.sql'

new docker-compose.yml

    wordpress:
      image: wordpress:4.1-apache
      external_links:
        - mysqlinfra_mysqlserver_1:mysql
      environment:
        - WORDPRESS_DB_NAME=wpfoo
      ports:
        - 8888:80
      volumes_from:
        - foo_wp_data

####research
hello.sql

      SHOW DATABASES;
      EXIT

this could easily be a mysql data dump

      docker run -it --rm --link mysqlinfra_mysqlserver_1:mysql -v /home/booyaa/stage/sql:/temp mysql:5.6 sh -c 'mysql --host=mysql --password="$MYSQL_ENV_MYSQL_ROOT_PASSWORD" < /temp/hello.sql'

how to reference wordpress locally on host (would prefer a data tainer)

    docker run --name myblog -v /home/booyaa/stage/wordpress:/var/www/html --link mysqlinfra_mysqlserver_1:mysql -p 8000:80 -e WORDPRESS_DB_NAME=wpfizz wordpress:4.1-apache


text:
Stand-alone

Name: mongodb

Requirement: Ubuntu 12.04 or later

Exposes ports: 127.0.0.1:27017

Installation notes: No

Installs the official MongoDB Docker container. MongoDB's persistent data is stored under /var/lib/mongodb in the VM.

Cluster

Name: mongodb

Requirement:

Ubuntu 12.04 or later

Three VMs

Weave module already deployed

Exposes ports: 127.0.0.1:27017

Additional required keys:

extra_vars['is_rs']: Set to True to initiate replica set configuration of MongoDB.

extra_vars['is_rs_master']: Set to True to initiate the server as a the master (primary) node.

extra_vars['is_rs_slave']: Set to True for all slaves (secondaries).

extra_vars['rs_init']: Set to True to initialize the cluster (must be after the cluster has been fully initiated).

MongoDB can also be deployed in a cluster setup over a Weave network. See Examples for a detailed example of this setup.


text:
package tls

import (
	"crypto/x509"
	"fmt"
	"sync"

	k8sv1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/cert"

	"kubevirt.io/client-go/log"

	"kubevirt.io/kubevirt/pkg/util"
	"kubevirt.io/kubevirt/pkg/virt-operator/resource/generate/components"
)

type ClientCAManager interface {
	GetCurrent() (*x509.CertPool, error)
}

type manager struct {
	store        cache.Store
	lock         *sync.Mutex
	lastRevision string
	namespace    string
	name         string
	secretKey    string

	lastPool *x509.CertPool
}

func NewKubernetesClientCAManager(configMapCache cache.Store) ClientCAManager {
	return &manager{
		store:        configMapCache,
		lock:         &sync.Mutex{},
		namespace:    metav1.NamespaceSystem,
		name:         util.ExtensionAPIServerAuthenticationConfigMap,
		secretKey:    util.RequestHeaderClientCAFileKey,
		lastRevision: "-1",
	}
}

func NewCAManager(configMapCache cache.Store, namespace string, configMapName string) ClientCAManager {
	return &manager{
		store:        configMapCache,
		lock:         &sync.Mutex{},
		namespace:    namespace,
		name:         configMapName,
		secretKey:    components.CABundleKey,
		lastRevision: "-1",
	}
}

func (m *manager) GetCurrent() (*x509.CertPool, error) {
	m.lock.Lock()
	defer m.lock.Unlock()

	obj, exists, err := m.store.GetByKey(m.namespace + "/" + m.name)

	if err != nil {
		return nil, err
	} else if !exists {
		if m.lastPool != nil {
			return m.lastPool, nil
		}

		return nil, fmt.Errorf("configmap %s not found. Unable to detect request header CA", m.name)
	}

	configMap := obj.(*k8sv1.ConfigMap)

	// no change detected.
	if m.lastRevision == configMap.ResourceVersion {
		return m.lastPool, nil
	}
	log.DefaultLogger().Infof("CA update in configmap %s/%s detected. Updating from resource version %v to %v", m.namespace, m.name, m.lastRevision, configMap.ResourceVersion)

	requestHeaderClientCA, ok := configMap.Data[m.secretKey]
	if !ok {
		return nil, fmt.Errorf("requestheader-client-ca-file not found in configmap %s/%s", m.namespace, m.name)
	}

	certs, err := cert.ParseCertsPEM([]byte(requestHeaderClientCA))
	if err != nil {
		return nil, err
	}
	pool := x509.NewCertPool()
	for _, cert := range certs {
		pool.AddCert(cert)
	}

	m.lastRevision = configMap.ResourceVersion
	m.lastPool = pool

	return pool, nil
}


text:
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DbProvider.cs" company="">
//
// </copyright>
// <summary>
//   The db provider.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

#region using directives

using System;
using System.Data;
using System.Data.Common;

using RabbitDB.Contracts.Query;
using RabbitDB.Contracts.SqlDialect;
using RabbitDB.Contracts.Storage;
using RabbitDB.Query;

#endregion

namespace RabbitDB.Storage
{
    /// <summary>
    ///     The db provider.
    /// </summary>
    internal abstract class DbProvider : IDbProvider
    {
        #region Fields

        /// <summary>
        ///     The connection string.
        /// </summary>
        private readonly string _connectionString;

        /// <summary>
        ///     The database provider factory.
        /// </summary>
        private readonly DbProviderFactory _dbFactory;

        /// <summary>
        ///     The database command.
        /// </summary>
        protected IDbCommand DbCommand;

        /// <summary>
        ///     The database connection.
        /// </summary>
        protected IDbConnection DbConnection;

        /// <summary>
        ///     The database transaction.
        /// </summary>
        protected IDbTransaction DbTransaction;

        #endregion

        #region Construction

        /// <summary>
        ///     Initializes a new instance of the <see cref="DbProvider" /> class.
        /// </summary>
        /// <param name="connectionString">
        ///     The connection string.
        /// </param>
        protected DbProvider(string connectionString)
        {
            // ReSharper disable once DoNotCallOverridableMethodsInConstructor
            if (ProviderName == null)
            {
                throw new NullReferenceException("Providername was null.");
            }

            // ReSharper disable once DoNotCallOverridableMethodsInConstructor
            _dbFactory = DbProviderFactories.GetFactory(ProviderName);
            _connectionString = connectionString;
        }

        #endregion

        #region  Properties

        /// <summary>
        ///     Gets the provider name.
        /// </summary>
        public abstract string ProviderName { get; }

        #endregion

        #region Public Methods

        /// <summary>
        ///     The create command.
        /// </summary>
        /// <returns>
        ///     The <see cref="IDbCommand" />.
        /// </returns>
        public IDbCommand CreateCommand()
        {
            return DbConnection.CreateCommand();
        }

        /// <summary>
        ///     The create connection.
        /// </summary>
        public void CreateConnection()
        {
            if (DbTransaction != null)
            {
                DbConnection = DbTransaction.Connection;
            }
            else
            {
                CreateNewConnection();
            }
        }

        /// <summary>
        ///     The dispose.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);

            GC.SuppressFinalize(this);
        }

        public void Dispose(bool disposing)
        {
            if (disposing == false)
            {
                return;
            }

            DisposeConnection();
            DisposeCommand();
            DisposeTransaction();
        }

        /// <summary>
        ///     The prepare command.
        /// </summary>
        /// <param name="query">
        ///     The query.
        /// </param>
        /// <param name="sqlDialect">
        ///     The sql dialect.
        /// </param>
        /// <returns>
        ///     The <see cref="IDbCommand" />.
        /// </returns>
        public IDbCommand PrepareCommand(IQuery query, ISqlDialect sqlDialect)
        {
            CreateConnection();

            IDbCommand dbCommand = query.Compile(sqlDialect);

            dbCommand.Transaction = DbTransaction;

            return dbCommand;
        }

        #endregion

        #region Private Methods

        /// <summary>
        ///     The create new connection.
        /// </summary>
        private void CreateNewConnection()
        {
            if (DbConnection != null && DbConnection.State == ConnectionState.Open)
            {
                return;
            }

            DbConnection = _dbFactory.CreateConnection();

            // ReSharper disable once PossibleNullReferenceException
            DbConnection.ConnectionString = _connectionString;
            DbConnection.Open();
        }

        /// <summary>
        ///     The dispose command.
        /// </summary>
        private void DisposeCommand()
        {
            if (DbCommand == null)
            {
                return;
            }

            DbCommand.Dispose();
            DbCommand = null;
        }

        /// <summary>
        ///     The dispose connection.
        /// </summary>
        private void DisposeConnection()
        {
            if (InTransactionMode() || DbConnection == null)
            {
                return;
            }

            DbConnection.Close();
            DbConnection.Dispose();
            DbConnection = null;
        }

        /// <summary>
        ///     The dispose transaction.
        /// </summary>
        private void DisposeTransaction()
        {
            if (InTransactionMode() || DbTransaction == null)
            {
                return;
            }

            DbTransaction.Dispose();
            DbTransaction = null;
        }

        /// <summary>
        ///     The in transaction mode.
        /// </summary>
        /// <returns>
        ///     The <see cref="bool" />.
        /// </returns>
        private bool InTransactionMode()
        {
            return DbTransaction?.Connection != null;
        }

        #endregion
    }
}


text:
using namespace std;

Context* Context::singleton = nullptr;

// Constructors {{{
Context* Context::connect (string title) {

  if (singleton == nullptr) {
    singleton = new Context(title);
    singleton->init();
    singleton->run();
  }

  return singleton;
}

Context* Context::connect () {

  if (singleton == nullptr) {
    singleton = new Context();
    singleton->init();
    //singleton->run();
  }

  return singleton;
}

Context::Context(string s): settings(s), work(io) {
  init();
}

Context::Context(): work(io) {
  init();
}

Context::~Context() {
}
// }}}
// run {{{
void Context::run (){
  int concurrency = settings.get<int> ("cache.concurrency");
  for (int i = 0; i < concurrency; i++ ) {
    auto t = new std::thread ( [this] {
          try {
            this->io.run();

          } catch (exception& e) {
            logger->error("iosvc exception %s", e.what());
          } catch (boost::exception& e) {
            logger->error("iosvc exception %s", diagnostic_information(e).c_str());
          }
        });

    threads.emplace_back (t);
  }
}
// }}}
// join {{{
bool Context::join () {
  for (auto& t : threads) t->join();
  return true;
}
// }}}
// init {{{
void Context::init() {
  settings.load();
  string logname = settings.get<string> ("log.name");
  string logtype = settings.get<string> ("log.type");
  string logmask = settings.get<string> ("log.mask");
  string ip = settings.getip();
  vector<string> nodes  = settings.get<vector<string>> ("network.nodes");
  logger.reset(Logger::connect(logname, logtype, logmask));

  id = find(nodes.begin(), nodes.end(), ip) - nodes.begin();
}
// }}}


text:
export function createElement(options) {
  let tagName = 'div';

  if (options && options.tagName) {
    tagName = options.tagName;
  }

  let element = document.createElement(tagName);

  if (options) {
    if (options.style) {
      element.style = options.style;
    }

    if (options.className) {
      element.className = options.className;
    }

    if (options.textContent) {
      element.textContent = options.textContent;
    }

    if (options.placeholder) {
      element.placeholder = options.placeholder;
    }

    if (options.readonly) {
      element.readonly = true;
    }

    if (options.href) {
      element.href = options.href;
    }

    if (options.target) {
      element.target = options.target;
    }

    if (options.title) {
      element.title = options.title;
    }

    if (options.onclick) {
      element.addEventListener('click', (e) => options.onclick(e, options.component));
    }

    if (options.onchange) {
      element.addEventListener('change', (e) => options.onchange(e, options.component));
    }

    if (options.oninput) {
      element.addEventListener('input', (e) => options.oninput(e, options.component));
    }

    if (options.container) {
      options.container.appendChild(element);
    }
  }

  return element;
}

export function hideElement(element) {
  element.classList.add('hidden');
}

export function showElement(element) {
  element.classList.remove('hidden');
}

export function scrollToBottom(element) {
  element.scrollTop = element.scrollHeight;
}

export function scrolledToBottom(element) {
  return element.scrollHeight - element.clientHeight === element.scrollTop;
}

export function addTextToRow(row, text) {
  let cell = row.insertCell();

  cell.appendChild(document.createTextNode(text));

  return cell;
}

export function addElementToRow(row, element) {
  let cell = row.insertCell();

  cell.appendChild(element);

  return cell;
}

export function clearSelect(element) {
  for (let l = element.options.length - 1, i = l; i >= 0; i--) {
    element.remove(i);
  }
}


text:
trap 'INT' do
  Berkshelf::API::Application.shutdown
end

trap 'TERM' do
  Berkshelf::API::Application.shutdown
end

module Berkshelf::API
  class ApplicationSupervisor < Celluloid::SupervisionGroup
    # @option options [Boolean] :disable_http (false)
    #   run the application without the rest gateway
    def initialize(registry, options = {})
      super(registry)
      supervise_as(:cache_manager, Berkshelf::API::CacheManager)
      supervise_as(:cache_builder, Berkshelf::API::CacheBuilder)

      unless options[:disable_http]
        require_relative 'rest_gateway'
        supervise_as(:rest_gateway, Berkshelf::API::RESTGateway, options)
      end
    end
  end

  module Application
    class << self
      extend Forwardable
      include Berkshelf::API::Logging
      include Berkshelf::API::Mixin::Services

      attr_reader :start_time
      def_delegators :registry, :[], :[]=

      # @return [Berkshelf::API::Config]
      def config
        @config ||= begin
          Berkshelf::API::Config.from_file(Berkshelf::API::Config.default_path)
        rescue
          Berkshelf::API::Config.new
        end
      end

      # @param [Berkshelf::API::Config] config
      def set_config(config)
        @config = config
      end

      # Configure the application
      #
      # @option options [String] :config_file
      #   filepath to a configuration file to use
      # @option options [String, Fixnum] :log_location (STDOUT)
      # @option options [String, nil] :log_level ("INFO")
      #   - "DEBUG
      #   - "INFO"
      #   - "WARN"
      #   - "ERROR"
      #   - "FATAL"
      #
      # @raise [Berkshelf::API::ConfigNotFoundError]
      #
      # @return [Berkshelf::API::Config]
      def configure(options = {})
        unless options[:config_file].nil?
          set_config Berkshelf::API::Config.from_file(options[:config_file])
        end

        configure_logger(options)
        config
      rescue Buff::Errors::ConfigNotFound => ex
        raise ConfigNotFoundError, ex
      end

      # @option options [String, Fixnum] :log_location (STDOUT)
      # @option options [String, nil] :log_level ("INFO")
      #   - "DEBUG
      #   - "INFO"
      #   - "WARN"
      #   - "ERROR"
      #   - "FATAL"
      def configure_logger(options = {})
        Logging.init(level: options[:log_level], location: options[:log_location])
      end

      # @return [String]
      def home_path
        ENV['BERKSHELF_API_PATH'] || config.home_path
      end

      # Retrieve the running instance of the Application
      #
      # @raise [Berkshelf::API::NotStartedError]
      #
      # @return [Berkshelf::API::Application]
      def instance
        return @instance if @instance

        raise NotStartedError, "application not running"
      end

      # The Actor registry for Berkshelf::API.
      #
      # @note Berkshelf::API uses it's own registry instead of Celluloid::Registry.root to
      #   avoid conflicts in the larger namespace. Use Berkshelf::API::Application[] to access Berkshelf::API
      #   actors instead of Celluloid::Actor[].
      #
      # @return [Celluloid::Registry]
      def registry
        @registry ||= Celluloid::Registry.new
      end

      # Run the application in the foreground (sleep on main thread)
      #
      # @option options [Boolean] :disable_http (false)
      #   run the application without the rest gateway
      def run(options = {})
        @start_time = Time.now.utc
        loop do
          supervisor = run!(options)

          while supervisor.alive?
            sleep 0.1
            instance.terminate if @shutdown
          end

          break if @shutdown

          log.error "!!! #{self} crashed. Restarting..."
        end
      end

      # Run the application in the background
      #
      # @option options [Boolean] :disable_http (false)
      #   run the application without the rest gateway
      # @option options [Boolean] :eager_build (false)
      #   automatically begin and loop all cache builders
      #
      # @return [Berkshelf::API::Application]
      def run!(options = {})
        options = { disable_http: false, eager_build: false }.merge(options)
        configure(options)
        @instance = ApplicationSupervisor.new(registry, options)
        cache_builder.async(:build_loop) if options[:eager_build]
        @instance
      end

      # @return [Boolean]
      def running?
        instance.alive?
      rescue NotStartedError
        false
      end

      # Shutdown the running instance
      #
      # @raise [Berkshelf::API::NotStartedError]
      #   if there is no running instance
      def shutdown
        @shutdown = true
      end
    end
  end
end