text:
package alluxio.master.file.meta;

import alluxio.AlluxioURI;
import alluxio.exception.AccessControlException;
import alluxio.exception.ExceptionMessage;
import alluxio.exception.FileAlreadyExistsException;
import alluxio.exception.InvalidPathException;
import alluxio.exception.status.NotFoundException;
import alluxio.exception.status.UnavailableException;
import alluxio.master.file.meta.options.MountInfo;
import alluxio.master.file.options.MountOptions;
import alluxio.master.journal.JournalContext;
import alluxio.master.journal.JournalEntryIterable;
import alluxio.master.journal.JournalEntryReplayable;
import alluxio.proto.journal.File;
import alluxio.proto.journal.File.AddMountPointEntry;
import alluxio.proto.journal.File.DeleteMountPointEntry;
import alluxio.proto.journal.File.StringPairEntry;
import alluxio.proto.journal.Journal;
import alluxio.proto.journal.Journal.JournalEntry;
import alluxio.resource.CloseableResource;
import alluxio.resource.LockResource;
import alluxio.underfs.UfsManager;
import alluxio.underfs.UnderFileSystem;
import alluxio.util.IdUtils;
import alluxio.util.io.PathUtils;

import com.google.common.base.Throwables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;

/**
 * This class is used for keeping track of Alluxio mount points.
 */
@ThreadSafe
public final class MountTable implements JournalEntryIterable, JournalEntryReplayable {
  private static final Logger LOG = LoggerFactory.getLogger(MountTable.class);

  public static final String ROOT = "/";

  private final Lock mReadLock;
  private final Lock mWriteLock;

  /** Mount table state that is preserved across restarts. */
  @GuardedBy("mReadLock,mWriteLock")
  private final State mState;

  /** The manager of all ufs. */
  private final UfsManager mUfsManager;

  /**
   * Creates a new instance of {@link MountTable}.
   *
   * @param ufsManager the UFS manager
   * @param rootMountInfo root mount info
   */
  public MountTable(UfsManager ufsManager, MountInfo rootMountInfo) {
    mState = new State(rootMountInfo);
    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    mReadLock = lock.readLock();
    mWriteLock = lock.writeLock();
    mUfsManager = ufsManager;
  }

  @Override
  public Iterator<Journal.JournalEntry> getJournalEntryIterator() {
    final Iterator<Map.Entry<String, MountInfo>> it = mState.getMountTable().entrySet().iterator();
    return new Iterator<Journal.JournalEntry>() {
      /** mEntry is always set to the next non-root mount point if exists. */
      private Map.Entry<String, MountInfo> mEntry = null;

      @Override
      public boolean hasNext() {
        if (mEntry != null) {
          return true;
        }
        if (it.hasNext()) {
          mEntry = it.next();
          // Skip the root mount point, which is considered a part of initial state, not journaled
          // state.
          if (mEntry.getKey().equals(ROOT)) {
            mEntry = null;
            return hasNext();
          }
          return true;
        }
        return false;
      }

      @Override
      public Journal.JournalEntry next() {
        if (!hasNext()) {
          throw new NoSuchElementException();
        }
        String alluxioPath = mEntry.getKey();
        MountInfo info = mEntry.getValue();
        mEntry = null;

        Map<String, String> properties = info.getOptions().getProperties();
        List<File.StringPairEntry> protoProperties = new ArrayList<>(properties.size());
        for (Map.Entry<String, String> property : properties.entrySet()) {
          protoProperties.add(File.StringPairEntry.newBuilder()
              .setKey(property.getKey())
              .setValue(property.getValue())
              .build());
        }

        AddMountPointEntry addMountPoint =
            AddMountPointEntry.newBuilder().setAlluxioPath(alluxioPath)
                .setMountId(info.getMountId()).setUfsPath(info.getUfsUri().toString())
                .setReadOnly(info.getOptions().isReadOnly()).addAllProperties(protoProperties)
                .setShared(info.getOptions().isShared()).build();
        return Journal.JournalEntry.newBuilder().setAddMountPoint(addMountPoint).build();
      }

      @Override
      public void remove() {
        throw new UnsupportedOperationException("Mountable#Iterator#remove is not supported.");
      }
    };
  }

  /**
   * Mounts the given UFS path at the given Alluxio path. The Alluxio path should not be nested
   * under an existing mount point.
   *
   * @param journalContext the journal context
   * @param alluxioUri an Alluxio path URI
   * @param ufsUri a UFS path URI
   * @param mountId the mount id
   * @param options the mount options
   * @throws FileAlreadyExistsException if the mount point already exists
   * @throws InvalidPathException if an invalid path is encountered
   */
  public void add(Supplier<JournalContext> journalContext, AlluxioURI alluxioUri, AlluxioURI ufsUri,
      long mountId, MountOptions options) throws FileAlreadyExistsException, InvalidPathException {
    String alluxioPath = alluxioUri.getPath().isEmpty() ? "/" : alluxioUri.getPath();
    LOG.info("Mounting {} at {}", ufsUri, alluxioPath);

    try (LockResource r = new LockResource(mWriteLock)) {
      if (mState.getMountTable().containsKey(alluxioPath)) {
        throw new FileAlreadyExistsException(
            ExceptionMessage.MOUNT_POINT_ALREADY_EXISTS.getMessage(alluxioPath));
      }
      // Make sure that the ufs path we're trying to mount is not a prefix
      // or suffix of any existing mount path.
      for (Map.Entry<String, MountInfo> entry : mState.getMountTable().entrySet()) {
        AlluxioURI mountedUfsUri = entry.getValue().getUfsUri();
        if ((ufsUri.getScheme() == null || ufsUri.getScheme().equals(mountedUfsUri.getScheme()))
            && (ufsUri.getAuthority().toString().equals(mountedUfsUri.getAuthority().toString()))) {
          String ufsPath = ufsUri.getPath().isEmpty() ? "/" : ufsUri.getPath();
          String mountedUfsPath = mountedUfsUri.getPath().isEmpty() ? "/" : mountedUfsUri.getPath();
          if (PathUtils.hasPrefix(ufsPath, mountedUfsPath)) {
            throw new InvalidPathException(ExceptionMessage.MOUNT_POINT_PREFIX_OF_ANOTHER
                .getMessage(mountedUfsUri.toString(), ufsUri.toString()));
          }
          if (PathUtils.hasPrefix(mountedUfsPath, ufsPath)) {
            throw new InvalidPathException(ExceptionMessage.MOUNT_POINT_PREFIX_OF_ANOTHER
                .getMessage(ufsUri.toString(), mountedUfsUri.toString()));
          }
        }
      }
      mState.applyAndJournal(journalContext, AddMountPointEntry.newBuilder()
          .addAllProperties(options.getProperties().entrySet().stream()
              .map(entry -> StringPairEntry.newBuilder()
                  .setKey(entry.getKey()).setValue(entry.getValue()).build())
              .collect(Collectors.toList()))
          .setAlluxioPath(alluxioPath)
          .setMountId(mountId)
          .setReadOnly(options.isReadOnly())
          .setShared(options.isShared())
          .setUfsPath(ufsUri.toString())
          .build());
    }
  }

  /**
   * Clears all the mount points except the root.
   */
  public void reset() {
    LOG.info("Clearing mount table (except the root).");
    try (LockResource r = new LockResource(mWriteLock)) {
      mState.reset();
    }
  }

  /**
   * Unmounts the given Alluxio path. The path should match an existing mount point.
   *
   * @param journalContext journal context
   * @param uri an Alluxio path URI
   * @return whether the operation succeeded or not
   */
  public boolean delete(Supplier<JournalContext> journalContext, AlluxioURI uri) {
    String path = uri.getPath();
    LOG.info("Unmounting {}", path);
    if (path.equals(ROOT)) {
      LOG.warn("Cannot unmount the root mount point.");
      return false;
    }

    try (LockResource r = new LockResource(mWriteLock)) {
      if (mState.getMountTable().containsKey(path)) {
        // check if the path contains another nested mount point
        for (String mountPath : mState.getMountTable().keySet()) {
          try {
            if (PathUtils.hasPrefix(mountPath, path) && (!path.equals(mountPath))) {
              LOG.warn("The path to unmount {} contains another nested mountpoint {}",
                  path, mountPath);
              return false;
            }
          } catch (InvalidPathException e) {
            LOG.warn("Invalid path {} encountered when checking for nested mount point", path);
          }
        }
        mUfsManager.removeMount(mState.getMountTable().get(path).getMountId());
        mState.applyAndJournal(journalContext,
            DeleteMountPointEntry.newBuilder().setAlluxioPath(path).build());
        return true;
      }
      LOG.warn("Mount point {} does not exist.", path);
      return false;
    }
  }

  /**
   * Returns the closest ancestor mount point the given path is nested under.
   *
   * @param uri an Alluxio path URI
   * @return mount point the given Alluxio path is nested under
   * @throws InvalidPathException if an invalid path is encountered
   */
  public String getMountPoint(AlluxioURI uri) throws InvalidPathException {
    String path = uri.getPath();
    String lastMount = ROOT;
    try (LockResource r = new LockResource(mReadLock)) {
      for (Map.Entry<String, MountInfo> entry : mState.getMountTable().entrySet()) {
        String mount = entry.getKey();
        // we choose a new candidate path if the previous candidatepath is a prefix
        // of the current alluxioPath and the alluxioPath is a prefix of the path
        if (!mount.equals(ROOT) && PathUtils.hasPrefix(path, mount)
            && PathUtils.hasPrefix(mount, lastMount)) {
          lastMount = mount;
        }
      }
      return lastMount;
    }
  }

  /**
   * Returns a copy of the current mount table, the mount table is a map from Alluxio file system
   * URIs to the corresponding mount point information.
   *
   * @return a copy of the current mount table
   */
  public Map<String, MountInfo> getMountTable() {
    try (LockResource r = new LockResource(mReadLock)) {
      return new HashMap<>(mState.getMountTable());
    }
  }

  /**
   * @param uri the Alluxio uri to check
   * @return true if the specified uri is mount point, or has a descendant which is a mount point
   */
  public boolean containsMountPoint(AlluxioURI uri) throws InvalidPathException {
    String path = uri.getPath();

    try (LockResource r = new LockResource(mReadLock)) {
      for (Map.Entry<String, MountInfo> entry : mState.getMountTable().entrySet()) {
        String mountPath = entry.getKey();
        if (PathUtils.hasPrefix(mountPath, path)) {
          return true;
        }
      }
    }
    return false;
  }

  /**
   * @param uri an Alluxio path URI
   * @return whether the given uri is a mount point
   */
  public boolean isMountPoint(AlluxioURI uri) {
    try (LockResource r = new LockResource(mReadLock)) {
      return mState.getMountTable().containsKey(uri.getPath());
    }
  }

  private AlluxioURI reverseResolve(AlluxioURI mountPoint,
      AlluxioURI ufsUriMountPoint, AlluxioURI ufsUri)
      throws InvalidPathException {
    String relativePath = PathUtils.subtractPaths(
        PathUtils.normalizePath(ufsUri.getPath(), AlluxioURI.SEPARATOR),
        PathUtils.normalizePath(ufsUriMountPoint.getPath(), AlluxioURI.SEPARATOR));
    if (relativePath.isEmpty()) {
      return mountPoint;
    } else {
      return mountPoint.join(relativePath);
    }
  }

  /**
   * REsolves the given Ufs path. If the given UFs path is mounted in Alluxio space, it returns
   * the associated Alluxio path.
   * @param ufsUri an Ufs path URI
   * @return an Alluxio path URI
   */
  public AlluxioURI reverseResolve(AlluxioURI ufsUri) {
    AlluxioURI returnVal = null;
    for (Map.Entry<String, MountInfo> mountInfoEntry : getMountTable().entrySet()) {
      try {
        if (mountInfoEntry.getValue().getUfsUri().isParentOf(ufsUri)) {
          returnVal = reverseResolve(mountInfoEntry.getValue().getAlluxioUri(),
              mountInfoEntry.getValue().getUfsUri(), ufsUri);
        }
      } catch (InvalidPathException | RuntimeException e) {
        LOG.info(Throwables.getStackTraceAsString(e));
        // expected when ufsUri does not belong to this particular mountPoint
      }
      if (returnVal != null) {
        return returnVal;
      }
    }
    return null;
  }

  /**
   * Get the associated ufs client with the mount id.
   * @param mountId mount id to look up ufs client
   * @return ufsClient
   */
  public UfsManager.UfsClient getUfsClient(long mountId) {
    try {
      return mUfsManager.get(mountId);
    } catch (NotFoundException | UnavailableException e) {
      LOG.warn("failed to get ufsclient for mountid {}, exception {}", mountId, e);
    }
    return null;
  }

  /**
   * Resolves the given Alluxio path. If the given Alluxio path is nested under a mount point, the
   * resolution maps the Alluxio path to the corresponding UFS path. Otherwise, the resolution is a
   * no-op.
   *
   * @param uri an Alluxio path URI
   * @return the {@link Resolution} representing the UFS path
   * @throws InvalidPathException if an invalid path is encountered
   */
  public Resolution resolve(AlluxioURI uri) throws InvalidPathException {
    try (LockResource r = new LockResource(mReadLock)) {
      String path = uri.getPath();
      LOG.debug("Resolving {}", path);
      // This will re-acquire the read lock, but that is allowed.
      String mountPoint = getMountPoint(uri);
      if (mountPoint != null) {
        MountInfo info = mState.getMountTable().get(mountPoint);
        AlluxioURI ufsUri = info.getUfsUri();
        UfsManager.UfsClient ufsClient;
        AlluxioURI resolvedUri;
        try {
          ufsClient = mUfsManager.get(info.getMountId());
          try (CloseableResource<UnderFileSystem> ufsResource = ufsClient.acquireUfsResource()) {
            UnderFileSystem ufs = ufsResource.get();
            resolvedUri = ufs.resolveUri(ufsUri, path.substring(mountPoint.length()));
          }
        } catch (NotFoundException | UnavailableException e) {
          throw new RuntimeException(
              String.format("No UFS information for %s for mount Id %d, we should never reach here",
                  uri, info.getMountId()), e);
        }
        return new Resolution(resolvedUri, ufsClient, info.getOptions().isShared(),
            info.getMountId());
      }
      // TODO(binfan): throw exception as we should never reach here
      return new Resolution(uri, null, false, IdUtils.INVALID_MOUNT_ID);
    }
  }

  /**
   * Checks to see if a write operation is allowed for the specified Alluxio path, by determining
   * if it is under a readonly mount point.
   *
   * @param alluxioUri an Alluxio path URI
   * @throws InvalidPathException if the Alluxio path is invalid
   * @throws AccessControlException if the Alluxio path is under a readonly mount point
   */
  public void checkUnderWritableMountPoint(AlluxioURI alluxioUri)
      throws InvalidPathException, AccessControlException {
    try (LockResource r = new LockResource(mReadLock)) {
      // This will re-acquire the read lock, but that is allowed.
      String mountPoint = getMountPoint(alluxioUri);
      MountInfo mountInfo = mState.getMountTable().get(mountPoint);
      if (mountInfo.getOptions().isReadOnly()) {
        throw new AccessControlException(ExceptionMessage.MOUNT_READONLY, alluxioUri, mountPoint);
      }
    }
  }

  /**
   * @param mountId the given ufs id
   * @return the mount information with this id or null if this mount id is not found
   */
  @Nullable
  public MountInfo getMountInfo(long mountId) {
    try (LockResource r = new LockResource(mReadLock)) {
      for (Map.Entry<String, MountInfo> entry : mState.getMountTable().entrySet()) {
        if (entry.getValue().getMountId() == mountId) {
          return entry.getValue();
        }
      }
    }
    return null;
  }

  @Override
  public boolean replayJournalEntryFromJournal(JournalEntry entry) {
    return mState.replayJournalEntryFromJournal(entry);
  }

  /**
   * This class represents a UFS path after resolution. The UFS URI and the {@link UnderFileSystem}
   * for the UFS path are available.
   */
  public final class Resolution {
    private final AlluxioURI mUri;
    private final UfsManager.UfsClient mUfsClient;
    private final boolean mShared;
    private final long mMountId;

    private Resolution(AlluxioURI uri, UfsManager.UfsClient ufs, boolean shared, long mountId) {
      mUri = uri;
      mUfsClient = ufs;
      mShared = shared;
      mMountId = mountId;
    }

    /**
     * @return the URI in the ufs
     */
    public AlluxioURI getUri() {
      return mUri;
    }

    /**
     * @return the {@link UnderFileSystem} closeable resource
     */
    public CloseableResource<UnderFileSystem> acquireUfsResource() {
      return mUfsClient.acquireUfsResource();
    }

    /**
     * @return the shared option
     */
    public boolean getShared() {
      return mShared;
    }

    /**
     * @return the id of this mount point
     */
    public long getMountId() {
      return mMountId;
    }
  }

  /**
   * Persistent mount table state. replayJournalEntryFromJournal should only be called during
   * journal replay. To modify the mount table, create a journal entry and call one of the
   * applyAndJournal methods.
   */
  public static final class State implements JournalEntryReplayable {
    /** Maps from Alluxio path string, to {@link MountInfo}. */
    private final Map<String, MountInfo> mMountTable;

    /**
     * @return an unmodifiable view of the mount table
     */
    public Map<String, MountInfo> getMountTable() {
      return Collections.unmodifiableMap(mMountTable);
    }

    /**
     * @param mountInfo root mount info
     */
    public State(MountInfo mountInfo) {
      mMountTable = new HashMap<>(10);
      mMountTable.put(MountTable.ROOT, mountInfo);
    }

    @Override
    public boolean replayJournalEntryFromJournal(JournalEntry entry) {
      if (entry.hasAddMountPoint()) {
        apply(entry.getAddMountPoint());
      } else if (entry.hasDeleteMountPoint()) {
        apply(entry.getDeleteMountPoint());
      } else {
        return false;
      }
      return true;
    }

    /**
     * @param context journal context
     * @param entry add mount point entry
     */
    public void applyAndJournal(Supplier<JournalContext> context, AddMountPointEntry entry) {
      apply(entry);
      context.get().append(JournalEntry.newBuilder().setAddMountPoint(entry).build());
    }

    /**
     * @param context journal context
     * @param entry delete mount point entry
     */
    public void applyAndJournal(Supplier<JournalContext> context, DeleteMountPointEntry entry) {
      apply(entry);
      context.get().append(JournalEntry.newBuilder().setDeleteMountPoint(entry).build());
    }

    private void apply(AddMountPointEntry entry) {
      MountInfo mountInfo = new MountInfo(new AlluxioURI(entry.getAlluxioPath()),
          new AlluxioURI(entry.getUfsPath()), entry.getMountId(), new MountOptions(entry));
      mMountTable.put(entry.getAlluxioPath(), mountInfo);
    }

    private void apply(DeleteMountPointEntry entry) {
      mMountTable.remove(entry.getAlluxioPath());
    }

    /**
     * Resets the mount table state.
     */
    public void reset() {
      MountInfo mountInfo = mMountTable.get(ROOT);
      mMountTable.clear();
      if (mountInfo != null) {
        mMountTable.put(ROOT, mountInfo);
      }
    }
  }
}


text:
package funnel
package elastic

import java.io.IOException
import java.util.concurrent.{ThreadFactory, Executors, ExecutorService}
import org.scalatest.{FlatSpec,Matchers}
import org.scalatest.concurrent._
import org.scalatest.time._
import scala.collection.mutable
import scala.concurrent.duration._
import scala.language.postfixOps
import scalaz.Kleisli
import scalaz.concurrent.Task
import scalaz.stream.Process

class FlattenedSpec extends FlatSpec with Matchers with Eventually {
  val F = ElasticFlattened(Monitoring.default, new Instruments(Monitoring.default))

  def point =
    Datapoint[Any](Key[Double](s"now/${java.util.UUID.randomUUID.toString}", Units.Count, "some description",
      Map(AttributeKeys.source -> "http://10.0.10.10/stream/now", AttributeKeys.kind -> "gauge")
    ), 3.14)

  def point2 =
    Datapoint[Any](Key[Stats](s"sliding/${java.util.UUID.randomUUID.toString}", Units.Count, "some description",
      Map(AttributeKeys.source -> "http://10.0.10.10/stream/now", AttributeKeys.kind -> "gauge")
    ), Stats(3.14))

  def point3 =
    Datapoint[Any](Key[Stats](s"sliding/file_system.%2F.use_percent", Units.Bytes(Units.Base.Kilo), "some description",
      Map(AttributeKeys.source -> "http://10.0.10.10/stream/now", AttributeKeys.kind -> "gauge")
    ), Stats(3.14))

  //use batch size one to be able to predict how many http requests we will see
  val sharedCfg = new ElasticCfg("http://some-host", "idx", "a", "yyyy.MM.ww", "template",
    Some("version.sbt"),
    http = null,
    List("previous"),
    subscriptionTimeout = 300.millis, bufferSize = 4096, batchSize = 1)

  case class MonitoringEnv(M: Monitoring, I: Instruments, EF: ElasticFlattened, H: InMemoryHttpLayer) {
    private def taskRun[A](t: Duration)(op: Task[A]): java.util.concurrent.atomic.AtomicBoolean = {
      val b = new java.util.concurrent.atomic.AtomicBoolean(false)
      op.runAsyncInterruptibly(t => (), b)
      b
    }

    private def processRun[A](t: Duration)(p: Process[Task, A]): java.util.concurrent.atomic.AtomicBoolean = {
      val b = new java.util.concurrent.atomic.AtomicBoolean(false)
      p.run.runAsyncInterruptibly(_ => (), b)
      b
    }

    // starts publishing process and returns function to stop it
    def publish(): () => Unit = {
      val s1 = taskRun(10.seconds)(EF.publish("env", "a1", "a2")(sharedCfg))
      val s2 = processRun(10.seconds)(M.dataDislodgement)

      () => {
        s1.set(true)
        s2.set(true)
        ()
      }
    }
  }

  private def daemonThreads(name: String) = new ThreadFactory {
    def newThread(r: Runnable) = {
      val t = Executors.defaultThreadFactory.newThread(r)
      t.setDaemon(true)
      t.setName(name)
      t
    }
  }

  def monitoringEnv(rules: Seq[Rule] = Rule.defaultRules(sharedCfg))(testCode: (MonitoringEnv) => Any): Unit =
    monitoringEnv(InMemoryHttpLayer(rules))(testCode)

  def monitoringEnv(httpLayer: InMemoryHttpLayer)(testCode: (MonitoringEnv) => Any): Unit = {
    val pool: ExecutorService = Executors.newFixedThreadPool(8, daemonThreads("test-threads"))

    val M = Monitoring.instance(ES = pool, windowSize = 50.millis)

    val instruments = new Instruments(M, bufferTime = 20 millis)
    val ef = ElasticFlattened(M, instruments, httpLayer)

    val env = MonitoringEnv(M, instruments, ef, httpLayer)

    try {
      testCode(env)
    } finally {
      pool.shutdownNow()
    }
  }

  "toJson" should "correctly render documents to json in the happy case" in {
    println {
      F.toJson("dev", "127.0.0.1", "local")(point3)
    }
  }

  "template provisioning" should "provision template if missing" in
    monitoringEnv(Seq(Rule.failedPUT(s"/_template/${sharedCfg.templateName}", 999))) {
      (env: MonitoringEnv) =>
        val thrown:HttpException = the [HttpException] thrownBy env.EF.publish("env", "a1", "a2")(sharedCfg).run
        thrown.code should equal (999)
  }

  it should "not try to provision template otherwise" in monitoringEnv() {
    (env: MonitoringEnv) =>
      //wait until we see first metric, by that time all template check logic should be completed

      //send test metric
      val c = env.I.counter("m/test")
      c.increment

      val stopPublish = env.publish()

      eventually (timeout(Span(5, Seconds))) { env.H.requests.find(r => r.method == "POST") shouldBe defined }

      stopPublish()

      env.H.requests.find(r => r.method == "HEAD") shouldBe defined //expect lookup for template but no attempt to overwrite
      env.H.requests.find(r => r.method == "PUT") shouldBe empty
  }

  "publish" should "post first document" in monitoringEnv() {
    (env: MonitoringEnv) =>
      val c = env.I.counter("m/test")
      c.increment

      val stopPublish = env.publish()

      eventually (timeout(Span(5, Seconds))) {
        env.H.requests.find(r => r.method == "POST" && r.contains("m.test")) shouldBe defined
      }

      stopPublish()
  }

  it should "continue posting documents periodically" in monitoringEnv() {
    (env: MonitoringEnv) =>
      val c = env.I.counter("m/test")
      c.increment

      val stopPublish = env.publish()

      //update metric few times (waiting for longer than buffer window) => expect to see documents emitted
      (1 to 3) foreach {
        i =>
          Thread.sleep(100)
          c.increment
      }

      eventually (timeout(Span(5, Seconds))) {
        env.H.requests.count(r => r.method == "POST" && r.contains("m.test")) should be > 3
      }

      stopPublish()
  }

  //simulating errors on some requests
  private def failingHttpLayer(t: Throwable):InMemoryHttpLayer = new InMemoryHttpLayer(mutable.ListBuffer(Rule.defaultRules(sharedCfg): _*)) {
    var cnt = 0

    override def http(v: HttpOp): Elastic.ES[String] = {
      cnt = cnt + 1
      cnt match {
        case 4 =>
          Kleisli.kleisli[Task, ElasticCfg, String]((cfg: ElasticCfg) => Task.fail(t))
        case _ => super.http(v)
      }
    }
  }

  it should "not stop after http error" in monitoringEnv(failingHttpLayer(HttpException(500))) {
    (env: MonitoringEnv) =>
      val c = env.I.counter("m/test")
      c.increment

      val stopPublish = env.publish()

      //update metric few times (waiting for longer than buffer window) => expect to see documents emitted
      (1 to 10) foreach {
        i =>
          Thread.sleep(100)
          c.increment
      }

      eventually (timeout(Span(5, Seconds))) {
        env.H.requests.count(r => r.method == "POST" && r.contains("m.test")) should be > 9
      }

      stopPublish()
  }

  it should "retry and proceed after non http error" in monitoringEnv(failingHttpLayer(new IOException("TestException"))) {
    (env: MonitoringEnv) =>
      val c = env.I.counter("m/test")
      c.increment

      val stopPublish = env.publish()

      //update metric few times (waiting for longer than buffer window) => expect to see documents emitted
      (1 to 10) foreach {
        i =>
          Thread.sleep(100)
          c.increment
      }

      eventually (timeout(Span(5, Seconds))) {
        env.H.requests.count(r => r.method == "POST" && r.contains("m.test")) should be > 10
      }

      stopPublish()
  }
}

redpajama_set_name: RedPajamaGithub


text:
<?xml version="1.0" encoding="UTF-8"?>
<Form xmlns="http://v8.1c.ru/8.3/xcf/logform" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<Title>
		<v8:item>
			<v8:lang>ru</v8:lang>
			<v8:content>Выбор документа расчетов с партнером</v8:content>
		</v8:item>
	</Title>
	<WindowOpeningMode>LockOwnerWindow</WindowOpeningMode>
	<AutoTitle>false</AutoTitle>
	<VerticalScroll>useIfNecessary</VerticalScroll>
	<CommandSet/>
	<AutoCommandBar name="ФормаКоманднаяПанель" id="-1">
		<Autofill>false</Autofill>
		<ChildItems>
			<Button name="ФормаВыбратьДокумент" id="24">
				<Type>CommandBarButton</Type>
				<DefaultButton>true</DefaultButton>
				<CommandName>Form.Command.ВыбратьДокумент</CommandName>
				<Parameter xsi:nil="true"/>
				<ExtendedTooltip name="ФормаВыбратьДокументExtendedTooltip" id="31"/>
			</Button>
			<ButtonGroup name="ФормаГруппаОткрыть" id="27">
				<Title>
					<v8:item>
						<v8:lang>ru</v8:lang>
						<v8:content>Форма группа открыть</v8:content>
					</v8:item>
				</Title>
				<ToolTip>
					<v8:item>
						<v8:lang>ru</v8:lang>
						<v8:content>Форма группа открыть</v8:content>
					</v8:item>
				</ToolTip>
				<ExtendedTooltip name="ФормаГруппаОткрытьExtendedTooltip" id="32"/>
				<ChildItems>
					<Button name="СписокОткрытьДокумент" id="26">
						<Type>CommandBarButton</Type>
						<CommandName>Form.Command.ОткрытьДокумент</CommandName>
						<Parameter xsi:nil="true"/>
						<ExtendedTooltip name="СписокОткрытьДокументExtendedTooltip" id="33"/>
					</Button>
				</ChildItems>
			</ButtonGroup>
			<ButtonGroup name="ГруппаФормы" id="25">
				<Title>
					<v8:item>
						<v8:lang>ru</v8:lang>
						<v8:content>Группа формы</v8:content>
					</v8:item>
				</Title>
				<ToolTip>
					<v8:item>
						<v8:lang>ru</v8:lang>
						<v8:content>Группа формы</v8:content>
					</v8:item>
				</ToolTip>
				<CommandSource>Form</CommandSource>
				<ExtendedTooltip name="ГруппаФормыExtendedTooltip" id="34"/>
			</ButtonGroup>
		</ChildItems>
	</AutoCommandBar>
	<Events>
		<Event name="OnCreateAtServer">ПриСозданииНаСервере</Event>
	</Events>
	<ChildItems>
		<Table name="Список" id="1">
			<Representation>List</Representation>
			<CommandBarLocation>None</CommandBarLocation>
			<UseAlternationRowColor>true</UseAlternationRowColor>
			<InitialTreeView>ExpandTopLevel</InitialTreeView>
			<EnableStartDrag>true</EnableStartDrag>
			<DataPath>Список</DataPath>
			<RowPictureDataPath>Список.СостояниеДокумента</RowPictureDataPath>
			<RowsPicture>
				<xr:Ref>CommonPicture.СостоянияДокумента</xr:Ref>
				<xr:LoadTransparent>false</xr:LoadTransparent>
			</RowsPicture>
			<SearchStringLocation>None</SearchStringLocation>
			<ViewStatusLocation>None</ViewStatusLocation>
			<SearchControlLocation>None</SearchControlLocation>
			<AutoRefresh>false</AutoRefresh>
			<AutoRefreshPeriod>60</AutoRefreshPeriod>
			<Period xsi:type="v8:StandardPeriod">
				<v8:variant xsi:type="v8:StandardPeriodVariant">Custom</v8:variant>
				<v8:startDate>0001-01-01T00:00:00</v8:startDate>
				<v8:endDate>0001-01-01T00:00:00</v8:endDate>
			</Period>
			<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
			<RestoreCurrentRow>false</RestoreCurrentRow>
			<TopLevelParent xsi:nil="true"/>
			<ShowRoot>true</ShowRoot>
			<AllowRootChoice>false</AllowRootChoice>
			<UpdateOnDataChange>Auto</UpdateOnDataChange>
			<ContextMenu name="СписокКонтекстноеМеню" id="2">
				<ChildItems>
					<Button name="СписокКонтекстноеМенюОткрытьДокумент" id="28">
						<Type>CommandBarButton</Type>
						<CommandName>Form.Command.ОткрытьДокумент</CommandName>
						<Parameter xsi:nil="true"/>
						<ExtendedTooltip name="СписокКонтекстноеМенюОткрытьДокументExtendedTooltip" id="35"/>
					</Button>
				</ChildItems>
			</ContextMenu>
			<AutoCommandBar name="СписокКоманднаяПанель" id="3">
				<Autofill>false</Autofill>
			</AutoCommandBar>
			<ExtendedTooltip name="СписокExtendedTooltip" id="36"/>
			<SearchStringAddition name="СписокSearchString" id="37">
				<Source>
					<lf:elementId>1</lf:elementId>
					<lf:additionId>0</lf:additionId>
				</Source>
				<ContextMenu name="СписокSearchStringContextMenu" id="38"/>
				<ExtendedTooltip name="СписокSearchStringExtendedTooltip" id="39"/>
			</SearchStringAddition>
			<ViewStatusAddition name="СписокViewStatus" id="40">
				<Source>
					<lf:elementId>1</lf:elementId>
					<lf:additionId>1</lf:additionId>
				</Source>
				<ContextMenu name="СписокViewStatusContextMenu" id="41"/>
				<ExtendedTooltip name="СписокViewStatusExtendedTooltip" id="42"/>
			</ViewStatusAddition>
			<SearchControlAddition name="СписокSearchControl" id="43">
				<Source>
					<lf:elementId>1</lf:elementId>
					<lf:additionId>2</lf:additionId>
				</Source>
				<ContextMenu name="СписокSearchControlContextMenu" id="44"/>
				<ExtendedTooltip name="СписокSearchControlExtendedTooltip" id="45"/>
			</SearchControlAddition>
			<Events>
				<Event name="Selection">СписокВыбор</Event>
			</Events>
			<ChildItems>
				<LabelField name="Номер" id="6">
					<DataPath>Список.Номер</DataPath>
					<Width>10</Width>
					<ContextMenu name="НомерКонтекстноеМеню" id="7"/>
					<ExtendedTooltip name="НомерExtendedTooltip" id="46"/>
				</LabelField>
				<LabelField name="Дата" id="4">
					<DataPath>Список.Дата</DataPath>
					<Width>12</Width>
					<ContextMenu name="ДатаКонтекстноеМеню" id="5"/>
					<ExtendedTooltip name="ДатаExtendedTooltip" id="47"/>
				</LabelField>
				<LabelField name="Тип" id="29">
					<DataPath>Список.Тип</DataPath>
					<ContextMenu name="ТипКонтекстноеМеню" id="30"/>
					<ExtendedTooltip name="ТипExtendedTooltip" id="48"/>
				</LabelField>
				<LabelField name="Сумма" id="16">
					<DataPath>Список.Сумма</DataPath>
					<Width>1</Width>
					<Format>
						<v8:item>
							<v8:lang>ru</v8:lang>
							<v8:content>ЧЦ=15; ЧДЦ=2</v8:content>
						</v8:item>
					</Format>
					<ContextMenu name="СуммаКонтекстноеМеню" id="17"/>
					<ExtendedTooltip name="СуммаExtendedTooltip" id="49"/>
				</LabelField>
				<LabelField name="Валюта" id="18">
					<DataPath>Список.Валюта</DataPath>
					<Width>1</Width>
					<ContextMenu name="ВалютаКонтекстноеМеню" id="19"/>
					<ExtendedTooltip name="ВалютаExtendedTooltip" id="50"/>
				</LabelField>
				<LabelField name="Организация" id="10">
					<DataPath>Список.Организация</DataPath>
					<Width>1</Width>
					<ContextMenu name="ОрганизацияКонтекстноеМеню" id="11"/>
					<ExtendedTooltip name="ОрганизацияExtendedTooltip" id="51"/>
				</LabelField>
				<LabelField name="Партнер" id="12">
					<DataPath>Список.Партнер</DataPath>
					<Title>
						<v8:item>
							<v8:lang>ru</v8:lang>
							<v8:content>Партнер</v8:content>
						</v8:item>
					</Title>
					<Width>1</Width>
					<ContextMenu name="ПартнерКонтекстноеМеню" id="13"/>
					<ExtendedTooltip name="ПартнерExtendedTooltip" id="52"/>
				</LabelField>
				<LabelField name="Контрагент" id="14">
					<DataPath>Список.Контрагент</DataPath>
					<Width>1</Width>
					<ContextMenu name="КонтрагентКонтекстноеМеню" id="15"/>
					<ExtendedTooltip name="КонтрагентExtendedTooltip" id="53"/>
				</LabelField>
				<LabelField name="Ссылка" id="22">
					<DataPath>Список.Ссылка</DataPath>
					<Title>
						<v8:item>
							<v8:lang>ru</v8:lang>
							<v8:content>Документ</v8:content>
						</v8:item>
					</Title>
					<Width>1</Width>
					<ContextMenu name="СсылкаКонтекстноеМеню" id="23"/>
					<ExtendedTooltip name="СсылкаExtendedTooltip" id="54"/>
				</LabelField>
			</ChildItems>
		</Table>
	</ChildItems>
	<Attributes>
		<Attribute name="Список" id="1">
			<Title>
				<v8:item>
					<v8:lang>ru</v8:lang>
					<v8:content>Список</v8:content>
				</v8:item>
			</Title>
			<Type>
				<v8:Type>cfg:DynamicList</v8:Type>
			</Type>
			<MainAttribute>true</MainAttribute>
			<UseAlways>
				<Field>Список.Валюта</Field>
				<Field>Список.Партнер</Field>
				<Field>1/22</Field>
				<Field>Список.Номер</Field>
				<Field>Список.Дата</Field>
				<Field>Список.Ссылка</Field>
				<Field>Список.ОбъектРасчетов</Field>
			</UseAlways>
			<Settings xsi:type="DynamicList">
				<ManualQuery>true</ManualQuery>
				<DynamicDataRead>false</DynamicDataRead>
				<QueryText>ВЫБРАТЬ
	Неопределено КАК Ссылка,
	ДАТАВРЕМЯ(1,1,1) КАК Дата,
	"000000000000" КАК Номер,
	ЗНАЧЕНИЕ(Справочник.Организации.ПустаяСсылка) КАК Организация,
	ЗНАЧЕНИЕ(Справочник.Партнеры.ПустаяСсылка) КАК Партнер,
	ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка) КАК Контрагент,
	0 КАК Сумма,
	ЗНАЧЕНИЕ(Справочник.Валюты.ПустаяСсылка) КАК Валюта,
	Неопределено КАК Тип,
	Истина КАК ЭтоРасчетыСКлиентами,
	Неопределено КАК ОбъектРасчетов,
	0 КАК СостояниеДокумента

</QueryText>
				<ListSettings>
					<dcsset:filter>
						<dcsset:viewMode>Normal</dcsset:viewMode>
						<dcsset:userSettingID>dfcece9d-5077-440b-b6b3-45a5cb4538eb</dcsset:userSettingID>
					</dcsset:filter>
					<dcsset:order>
						<dcsset:item xsi:type="dcsset:OrderItemField">
							<dcsset:field>Дата</dcsset:field>
							<dcsset:orderType>Asc</dcsset:orderType>
						</dcsset:item>
						<dcsset:viewMode>Normal</dcsset:viewMode>
						<dcsset:userSettingID>88619765-ccb3-46c6-ac52-38e9c992ebd4</dcsset:userSettingID>
					</dcsset:order>
					<dcsset:conditionalAppearance>
						<dcsset:viewMode>Normal</dcsset:viewMode>
						<dcsset:userSettingID>b75fecce-942b-4aed-abc9-e6a02e460fb3</dcsset:userSettingID>
					</dcsset:conditionalAppearance>
				</ListSettings>
			</Settings>
		</Attribute>
		<ConditionalAppearance>
			<dcsset:item>
				<dcsset:selection>
					<dcsset:item>
						<dcsset:use>false</dcsset:use>
						<dcsset:field>Номер</dcsset:field>
					</dcsset:item>
					<dcsset:item>
						<dcsset:use>false</dcsset:use>
						<dcsset:field>Дата</dcsset:field>
					</dcsset:item>
					<dcsset:item>
						<dcsset:use>false</dcsset:use>
						<dcsset:field>Тип</dcsset:field>
					</dcsset:item>
					<dcsset:item>
						<dcsset:use>false</dcsset:use>
						<dcsset:field>Сумма</dcsset:field>
					</dcsset:item>
					<dcsset:item>
						<dcsset:use>false</dcsset:use>
						<dcsset:field>Валюта</dcsset:field>
					</dcsset:item>
					<dcsset:item>
						<dcsset:use>false</dcsset:use>
						<dcsset:field>Организация</dcsset:field>
					</dcsset:item>
					<dcsset:item>
						<dcsset:use>false</dcsset:use>
						<dcsset:field>Партнер</dcsset:field>
					</dcsset:item>
					<dcsset:item>
						<dcsset:use>false</dcsset:use>
						<dcsset:field>Контрагент</dcsset:field>
					</dcsset:item>
					<dcsset:item>
						<dcsset:use>false</dcsset:use>
						<dcsset:field>Соглашение</dcsset:field>
					</dcsset:item>
					<dcsset:item>
						<dcsset:use>false</dcsset:use>
						<dcsset:field>Соглашение</dcsset:field>
					</dcsset:item>
					<dcsset:item>
						<dcsset:use>false</dcsset:use>
						<dcsset:field>Ссылка</dcsset:field>
					</dcsset:item>
					<dcsset:item>
						<dcsset:field>Список</dcsset:field>
					</dcsset:item>
				</dcsset:selection>
				<dcsset:filter>
					<dcsset:item xsi:type="dcsset:FilterItemComparison">
						<dcsset:left xsi:type="dcscor:Field">Список.СостояниеДокумента</dcsset:left>
						<dcsset:comparisonType>NotEqual</dcsset:comparisonType>
						<dcsset:right xsi:type="xs:decimal">0</dcsset:right>
					</dcsset:item>
				</dcsset:filter>
				<dcsset:appearance>
					<dcscor:item xsi:type="dcsset:SettingsParameterValue">
						<dcscor:parameter>TextColor</dcscor:parameter>
						<dcscor:value xsi:type="v8ui:Color">style:ЦветТекстаОтмененнойСтрокиДокумента</dcscor:value>
					</dcscor:item>
				</dcsset:appearance>
			</dcsset:item>
		</ConditionalAppearance>
	</Attributes>
	<Commands>
		<Command name="ВыбратьДокумент" id="1">
			<Title>
				<v8:item>
					<v8:lang>ru</v8:lang>
					<v8:content>Выбрать</v8:content>
				</v8:item>
			</Title>
			<ToolTip>
				<v8:item>
					<v8:lang>ru</v8:lang>
					<v8:content>Выбрать документ</v8:content>
				</v8:item>
			</ToolTip>
			<Picture>
				<xr:Ref>StdPicture.ChooseValue</xr:Ref>
				<xr:LoadTransparent>true</xr:LoadTransparent>
			</Picture>
			<Action>ВыбратьДокумент</Action>
			<Representation>TextPicture</Representation>
			<ModifiesSavedData>true</ModifiesSavedData>
		</Command>
		<Command name="ОткрытьДокумент" id="2">
			<Title>
				<v8:item>
					<v8:lang>ru</v8:lang>
					<v8:content>Открыть документ</v8:content>
				</v8:item>
			</Title>
			<ToolTip>
				<v8:item>
					<v8:lang>ru</v8:lang>
					<v8:content>Открыть документ</v8:content>
				</v8:item>
			</ToolTip>
			<Shortcut>F2</Shortcut>
			<Picture>
				<xr:Ref>StdPicture.Change</xr:Ref>
				<xr:LoadTransparent>true</xr:LoadTransparent>
			</Picture>
			<Action>ОткрытьДокумент</Action>
		</Command>
	</Commands>
	<Parameters>
		<Parameter name="ЭтоРасчетыСКлиентами">
			<Type>
				<v8:Type>xs:boolean</v8:Type>
			</Type>
		</Parameter>
		<Parameter name="ВыборОснованияПлатежа">
			<Type>
				<v8:Type>xs:boolean</v8:Type>
			</Type>
		</Parameter>
	</Parameters>
</Form>


text:
ACCEPTED

#### According to
The Catalogue of Life, 3rd January 2011

#### Published in
null

#### Original name
Fimbristylis sieberiana Kunth

### Remarks
null


text:
<head>

  <title>
    Dapper Ink
  </title>

  <link href="//s3.amazonaws.com/images.dapperink/dapper/icons/favicon.ico" rel="shortcut icon" type="image/x-icon">
  <link href="//s3.amazonaws.com/images.dapperink/dapper/icons/apple.touch.small.png" rel="apple-touch-icon" type="image/x-icon">
  <link href="//s3.amazonaws.com/images.dapperink/dapper/icons/apple.touch.small.png" rel="apple-touch-icon" sizes="72x72" type="image/x-icon">
  <link href="//s3.amazonaws.com/images.dapperink/dapper/icons/apple.touch.medium.png" rel="apple-touch-icon" sizes="114x114" type="image/x-icon">
  <link href="//s3.amazonaws.com/images.dapperink/dapper/icons/apple.touch.medium.png" rel="shortcut icon" sizes="114x114">

  <meta charset="utf-8">
  <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
  <meta content="width=device-width" name="viewport">
  <meta content="telephone=no" name="format-detection">
  <meta content="user-scalable=1, width=device-width, initial-scale=1.0, maximum-scale=2.0" name="viewport">

  <meta name="referrer" content="origin">
  <meta name="description" content="We're pretty down-to-earth people with a passion for print & design. More importantly, we have a passion for helping people create an incredible product. We also love craft coffee, local food, and our Blue Ridge Mountains.">
  <meta itemprop="name" content="Dapper Ink">
  <meta itemprop="description" content="We're pretty down-to-earth people with a passion for print & design. More importantly, we have a passion for helping people create an incredible product. We also love craft coffee, local food, and our Blue Ridge Mountains.">
  <meta itemprop="image" content="https://s3.amazonaws.com/images.dapperink/dapper/site/images/dapperphoto1.jpg">
  <meta name="twitter:title" content="Dapper Ink">
  <meta name="twitter:description" content="We're pretty down-to-earth people with a passion for print & design. More importantly, we have a passion for helping people create an incredible product. We also love craft coffee, local food, and our Blue Ridge Mountains.">
	<meta name="twitter:image:src" content="https://s3.amazonaws.com/images.dapperink/dapper/site/images/dapperphoto1.jpg">
  <meta name="twitter:site" content="@DapperInk">
	<meta name="twitter:creator" content="@DapperInk">
  <meta property="og:title" content="Dapper Ink">
  <meta property="og:site_name" content="Dapper Ink" />
  <meta property="og:description" content="We're pretty down-to-earth people with a passion for print & design. More importantly, we have a passion for helping people create an incredible product. We also love craft coffee, local food, and our Blue Ridge Mountains.">
  <meta property="og:image" content="https://s3.amazonaws.com/images.dapperink/dapper/site/images/dapperphoto1.jpg">
  <meta property="og:url" content="https://dapperink.com">
  <meta property="og:site_label" content="Dapper Ink">

  <link href='//fonts.googleapis.com/css?family=Roboto+Condensed:400,700,300' rel='stylesheet' type='text/css'>
  <!-- <link href='//fonts.googleapis.com/css?family=Roboto:400,900,700,500' rel='stylesheet' type='text/css'> -->
  <link href='//fonts.googleapis.com/css?family=Neuton:400,400italic' rel='stylesheet' type='text/css'>

  <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">

</head>

<template name="page-layout">

  {{> Template.dynamic template=template data=data}}

</template>


text:
import { assign, map } from '../-private/helpers';
import { getExecutionContext } from '../-private/execution_context';

/**
 * @public
 *
 * Returns the value of a property from the matched element, or an array of
 * values from multiple matched elements.
 *
 * @example
 * // <input type="checkbox" checked="checked">
 *
 * import { create, property } from 'ember-cli-page-object';
 *
 * const page = create({
 *   isChecked: property('checked', 'input')
 * });
 *
 * assert.ok(page.isChecked);
 *
 * @example
 *
 * // <input type="checkbox" checked="checked">
 * // <input type="checkbox" checked="">
 *
 * import { create, property } from 'ember-cli-page-object';
 *
 * const page = create({
 *   inputsChecked: property('checked', 'input', { multiple: true })
 * });
 *
 * assert.deepEqual(page.inputsChecked, [true, false]);
 *
 * @example
 *
 * //
<input>

 * //
<input type="checkbox" checked="checked">

 * //
<input>

 *
 * import { create, property } from 'ember-cli-page-object';
 *
 * const page = create({
 *   isChecked: property('checked', 'input', { scope: '.scope' })
 * });
 *
 * assert.ok(page.isChecked);
 *
 * @public
 *
 * @param {string} propertyName - Name of the property to get
 * @param {string} selector - CSS selector of the element to check
 * @param {Object} options - Additional options
 * @param {string} options.scope - Nests provided scope within parent's scope
 * @param {boolean} options.resetScope - Override parent's scope
 * @param {number} options.at - Reduce the set of matched elements to the one at the specified index
 * @param {boolean} options.multiple - If set, the function will return an array of values
 * @return {Descriptor}
 *
 * @throws Will throw an error if no element matches selector
 * @throws Will throw an error if multiple elements are matched by selector and multiple option is not set
 */
export function property(propertyName, selector, userOptions = {}) {
  return {
    isDescriptor: true,

    get(key) {
      let executionContext = getExecutionContext(this);
      let options = assign({ pageObjectKey: key }, userOptions);

      return executionContext.run((context) => {
        let elements = context.findWithAssert(selector, options);
        let result;

        result = map(elements, function(element) {
          return element.prop(propertyName);
        });

        return options.multiple ? result : result[0];
      });
    }
  };
}


text:
> text <caret>goes
> other


text:
namespace Glimpse.Core.Model
{
    /// <summary>
    /// Colors that are used by the category model
    /// </summary>
    public class TimelineCategoryModel
    {
        /// <summary>
        /// Gets or sets the main bar border color.
        /// </summary>
        /// <value>The event color highlight.</value>
        public string EventColorHighlight { get; set; }

        /// <summary>
        /// Gets or sets the main bar color.
        /// </summary>
        /// <value>The color of the event.</value>
        public string EventColor { get; set; }
    }
}


text:
'use strict';

type DevToolsPluginConnection = {
  isAppActive: () => boolean,
  host: string,
  port: number,
};

type DevToolsPlugin = {
  connectToDevTools: (connection: DevToolsPluginConnection) => void,
};

let register = function () {
  // noop
};

if (__DEV__) {
  const AppState = require('AppState');
  const WebSocket = require('WebSocket');
  const {PlatformConstants} = require('NativeModules');
  const reactDevTools = require('react-devtools-core');

  register = function (plugin: DevToolsPlugin) {
    // Initialize dev tools only if the native module for WebSocket is available
    if (WebSocket.isAvailable) {
      // Don't steal the DevTools from currently active app.
      // Note: if you add any AppState subscriptions to this file,
      // you will also need to guard against `AppState.isAvailable`,
      // or the code will throw for bundles that don't have it.
      const isAppActive = () => AppState.currentState !== 'background';

      // Special case: Genymotion is running on a different host.
      const host = PlatformConstants && PlatformConstants.ServerHost ?
        PlatformConstants.ServerHost.split(':')[0] :
        'localhost';

      plugin.connectToDevTools({
        isAppActive,
        host,
        // Read the optional global variable for backward compatibility.
        // It was added in https://github.com/facebook/react-native/commit/bf2b435322e89d0aeee8792b1c6e04656c2719a0.
        port: window.__REACT_DEVTOOLS_PORT__,
        resolveRNStyle: require('flattenStyle'),
      });
    }
  };

  register(reactDevTools);
  global.registerDevtoolsPlugin = register;
}

module.exports = {
  register,
};


text:
title: Snake Man
baseurl: ../
date: 2016-02-21
categories: cognition communication
toolFile: snakeman.swf
toolWidth: 800
toolHeight: 600
navGroup: learningtools
---


text:
require "spec_helper"
require "rack/test"

describe "/api/v1/datastores", :type => :api do
  let!(:user) { FactoryBot.create(:user) }
  let!(:token) { user.authentication_token }
  let!(:datastore) { FactoryBot.create(:datastore) }
  let(:url) { "/api/v1/datastores" }

  context "projects viewable by this user" do
    it "json" do
      get "#{url}.json", :token => token, :user_email => user.email

      datastores_json = Datastore.all.to_json
      expect(last_response.body).to eql(datastores_json)
      expect(last_response.status).to eql(200)

      datastores = JSON.parse(last_response.body)

      datastores.any? do |ds|
        ds["name"] == datastore.name
      end
    end
  end

  context "show error when credentials are missing" do
    it "misses email and token" do
      get "#{url}.json"

      error = "{\"error\":\"You need to sign in or sign up before continuing.\"}"
      expect(last_response.body).to eql(error)
      expect(last_response.status).to eql(401)
    end
  end

  context "creating a project" do
    let(:doc) { IO.read(Rails.root.join("spec", "fixtures", "files", "cli_full.json")) }

    it "successful JSON" do
      post "#{url}.json", :token => token,
                          :user_email => user.email,
                          :datastore => {
                            :name => "Inspector"
                          }

      ds = Datastore.find_by_name!("Inspector")
      route = "/api/v1/datastores/#{ds.id}"
      expect(last_response.status).to eql(201)

      expect(last_response.headers["Location"]).to eql(route)
      expect(last_response.body).to eql(ds.to_json)
    end

    it "misses a name" do
      post "#{url}.json", :token => token,
                          :user_email => user.email,
                          :datastore => {
                            :mediatype => "DVD"
                          }

      expect(last_response.status).to eql(422)
      expect(last_response.body).to eql ('{"errors":{"name":["can\'t be blank"]}}')
    end

    it "can use filentory-cli output" do
      post "#{url}.json", :token => token,
                          :user_email => user.email,
                          :data => doc
      ds = Datastore.find_by_name!("testrun")
      route = "/api/v1/datastores/#{ds.id}"
      expect(last_response.status).to eq(201)

      expect(last_response.headers["Location"]).to eql(route)
      expect(last_response.body).to eq(ds.to_json)

      get "/api/v1/datastores/#{ds.id}.json", :token => token, :user_email => user.email

      store_response = JSON.parse(last_response.body)
      #puts store_response["locations"]

      first_location_name = store_response["locations"][0]["name"]
      expect(first_location_name).to eq("fileA.txt")

      #puts JSON.parse(store_response["files"])[0][0]["name"]
      #puts "-----"
      #puts JSON.parse(store_response["files"])[2]

      files = JSON.parse(store_response["files"])
      first_file = files[0][0]
      expect(first_file["name"]).to eql("fileA.txt")

      last_file = files[2][0]
      expect(last_file["name"]).to eql("video.mov")

      expect(files[2][1]["key"]).to eql("audio_bitrate")
    end
  end

end


text:
package muc;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.Scanner;

import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.Roster.SubscriptionMode;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Presence.Type;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smackx.muc.InvitationListener;
import org.jivesoftware.smackx.muc.MultiUserChat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.theholywaffle.lolchatapi.ChatServer;
import com.github.theholywaffle.lolchatapi.FriendRequestPolicy;
import com.github.theholywaffle.lolchatapi.LolChat;
import com.github.theholywaffle.lolchatapi.LolStatus;
import com.github.theholywaffle.lolchatapi.listeners.ChatListener;
import com.github.theholywaffle.lolchatapi.listeners.FriendListener;
import com.github.theholywaffle.lolchatapi.riotapi.RiotApiKey;
import com.github.theholywaffle.lolchatapi.wrapper.Friend;
import com.github.yeori.lol.listeners.MucListener;
import com.github.yeori.lol.muc.ChatRoom;
import com.github.yeori.lol.riotapi.DefaultRiotApiFactory;

public class MultiChatTester {
	static Logger logger = LoggerFactory.getLogger(MultiChatTester.class);
	public MultiChatTester() {

	}

	public static void main(String[] args) throws IOException {
		InputStream is = MultiChatTester.class.getResourceAsStream("/accounts.properties");
		Properties prop = new Properties();
		prop.load(is);
		is.close();
		String username = prop.getProperty("gamja.id");
		String password = prop.getProperty("gamja.pass");
		String key      = prop.getProperty("gamja.key");

		logger.debug("starting ...");
		LolChat api = new LolChat( ChatServer.KR,
				FriendRequestPolicy.MANUAL,
				new RiotApiKey(key),
				SSLSocketFactory.getDefault(),
				new DefaultRiotApiFactory());
		api.addFriendListener(new FriendHandler());
		api.addMultiUserChatListener(new MucHandler());
		if ( api.login(username, password, true) ) {

			api.addChatListener(new LolChatHandler());
			XMPPConnection conn = api.getConnection();
//			TestPacketFilter filter = new TestPacketFilter();
//
//			conn.addPacketListener(new IncomingPacketListener(), filter);
//			conn.addPacketSendingListener(new OutgoingPacketListener(), filter);
//			MultiUserChat.addInvitationListener(conn, new InvitationHandler());
			new Sender(api ).startThread();
		} else {
			logger.error("fail to login");
		}
	}

	static class MucHandler implements MucListener {

		@Override
		public boolean invitationReceived(LolChat chatApi, String roomName, String inviter,
				String password) {
			logger.info(String.format("[INVITATION] room:%s, inviter:%s, password:%s", roomName, inviter, password));
			ChatRoom room = chatApi.prepareChatRoom(roomName);
			return true;
		}

		@Override
		public void onMucMessage(Friend sender, String body) {

			logger.info(sender + ", " + body);

		}

	}
	static class FriendHandler implements FriendListener{

		@Override
		public void onFriendAvailable(Friend friend) {
			logger.info(String.format("[%s][AVAILABLE] 대화 가능",	friend.getName()));
			LolStatus ls = friend.getStatus();
			logger.debug(String.format("[MOBILE:%s]", ls.getMobile()));
			logger.debug(String.format("[status:%s]", ls.getStatusMessage()));
		}

		@Override
		public void onFriendAway(Friend friend) {
			logger.info(String.format("[%s][AWAY] 로그아웃했음", friend.getName()));

		}

		@Override
		public void onFriendBusy(Friend friend) {
			logger.info(String.format("[%s][BUSY] 바쁨으로 상태변경", friend.getName()));
		}

		@Override
		public void onFriendJoin(Friend friend) {
			logger.info(String.format("[%s][JOIN] 로그인했음", friend.getName()));

		}

		@Override
		public void onFriendLeave(Friend friend) {
			logger.info(String.format("[%s][LEAVE] 나갔음", friend.getName()));

		}

		@Override
		public void onFriendStatusChange(Friend friend) {
			logger.info(String.format("[%s][STATUS CHANGED] 상태변경", friend.getName()));

		}

		@Override
		public void onNewFriend(Friend friend) {
			logger.info(String.format("[%s][NEW] 새친구", friend.getName()));

		}

		@Override
		public void onRemoveFriend(String userId, String name) {
			logger.info(String.format("[ID:%s, NAME:%s][REMOVED] 친구 삭제",userId, name));
		}
	}

	static class LolChatHandler implements ChatListener {

		@Override
		public void onMessage(Friend friend, String message) {
			logger.debug(String.format("[%s]%s", friend.getName(), message));
		}

	}
	static class OutgoingPacketListener implements PacketListener {

		@Override
		public void processPacket(Packet packet) throws NotConnectedException {
			String from = packet.getFrom();
			String to = packet.getTo();
			String id = packet.getPacketID();
			XMPPError error = packet.getError();

			String fqClassName = packet.getClass().getName();
			if ( error != null ) {
				logger.debug("error", error);
			}
			logger.info("[OUT] " + packet.toXML().toString());
		}
	}

	static class IncomingPacketListener implements PacketListener {

		@Override
		public void processPacket(Packet packet) throws NotConnectedException {
			String from = packet.getFrom();
			String to = packet.getTo();
			String id = packet.getPacketID();
			XMPPError error = packet.getError();

			String fqClassName = packet.getClass().getName();
//			logger.debug(String.format("[type]%s", fqClassName.substring(fqClassName.lastIndexOf('.')+1)));
			if ( error != null ) {
				logger.debug("error", error);
			}
			logger.info("[IN ] "+ packet.toXML().toString());
		}
	}

	static class InvitationHandler implements InvitationListener {

		@Override
		public void invitationReceived(XMPPConnection conn, String room,
				String inviter, String reason, String password, Message message) {
			String nl = "\n        ";
			StringBuilder buf = new StringBuilder(nl);
			buf.append(String.format("ROOM   [%s]", room) + nl);
			buf.append(String.format("inviter[%s]", inviter) + nl);
			buf.append(String.format("reason [%s]", reason) + nl);
			buf.append(String.format("pass   [%s]", password) + nl);
			buf.append(String.format("message[%s]", message));

			logger.debug(buf.toString());

//			Presence joinPresence = new Presence(Type.available);
//			joinPresence.setTo(room);

			MultiUserChat muc = new MultiUserChat(conn, room);
			try {
				muc.join("감자깡이시다");

				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				try {
					muc.sendMessage("나 왔다.");
				} catch (XMPPException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
//				conn.sendPacket(joinPresence);

			} catch (NotConnectedException | NoResponseException | XMPPErrorException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();

			}
		}

	}
	static class Sender implements Runnable {
		static int packetId = 1000;
		XMPPConnection conn ;
		Roster roster ;
		String yangpa = "sum33172247@pvp.net/xiff";
		Thread thisThread ;
		LolChat chatApi;
		Sender ( LolChat chat) {
			this.chatApi = chat;
			this.conn = chat.getConnection();
			this.roster = conn.getRoster() ;
		}
		public void startThread() {
			thisThread = new Thread(this);
			thisThread.setName("T-SENDER");
			thisThread.start();
		}
		@Override
		public void run() {

			Scanner sc = new Scanner(System.in);

			String cmd = null;
			while ( true ){
				cmd = sc.nextLine();
				if ( "q".equals(cmd) ) {
					break;
				} else {
					Message msg = new Message(yangpa, Message.Type.chat);
					msg.setPacketID("sodkdkd" + nextId());
					try {
						msg.setBody(cmd);
						msg.setLanguage("ko");
						this.conn.sendPacket(msg);
					} catch (NotConnectedException e) {
						e.printStackTrace();
					}
				}
			}

			chatApi.disconnect();

			logger.debug("bye");
		}
		private int nextId() {
			return packetId++;
		}
	}

	static class TestPacketFilter implements PacketFilter {

		@Override
		public boolean accept(Packet packet) {

			return true;
		}

	}
}


text:
/* eslint-disable max-len */

import { renderGeneratedFileBanner } from '../utils/generatedFileBanner';
import { styleMixins } from '.';
import { GrafanaTheme2 } from '../../data';

export const lightThemeVarsTemplate = (theme: GrafanaTheme2) =>
  `${renderGeneratedFileBanner('grafana-ui/src/themes/light.ts', 'grafana-ui/src/themes/_variable.light.scss.tmpl.ts')}
// Global values
// --------------------------------------------------

$theme-name: light;

// New Colors
// -------------------------
$blue-light: ${theme.colors.primary.text};
$blue-base: ${theme.colors.primary.main};
$blue-shade: ${theme.colors.primary.shade};
$red-base: ${theme.colors.error.main};
$red-shade: ${theme.colors.error.shade};
$green-base: ${theme.colors.success.main};
$green-shade: ${theme.colors.success.shade};
$orange-dark: ${theme.v1.palette.orangeDark};

$gray98: ${theme.v1.palette.gray98};
$gray95: ${theme.v1.palette.gray95};
$gray85: ${theme.v1.palette.gray85};
$gray70: ${theme.v1.palette.gray70};
$gray60: ${theme.v1.palette.gray60};
$gray33: ${theme.v1.palette.gray33};
$gray25: ${theme.v1.palette.gray25};
$gray15: ${theme.v1.palette.gray15};
$gray10: ${theme.v1.palette.gray10};
$gray05: ${theme.v1.palette.gray05};

// Grays
// -------------------------
$black: ${theme.v1.palette.black};

$dark-1: ${theme.v1.palette.dark1};
$dark-2: ${theme.v1.palette.dark2};
$dark-4: ${theme.v1.palette.dark4};
$dark-10: ${theme.v1.palette.dark10};
$gray-1: ${theme.v1.palette.gray1};
$gray-2: ${theme.v1.palette.gray2};
$gray-3: ${theme.v1.palette.gray3};
$gray-4: ${theme.v1.palette.gray4};
$gray-5: ${theme.v1.palette.gray5};
$gray-6: ${theme.v1.palette.gray6};
$gray-7: ${theme.v1.palette.gray7};

$white: ${theme.v1.palette.white};

$layer0: ${theme.colors.background.canvas};
$layer1: ${theme.colors.background.primary};
$layer2: ${theme.colors.background.secondary};

$divider: ${theme.colors.border.weak};
$border0: ${theme.colors.border.weak};
$border1: ${theme.colors.border.medium};

// Accent colors
// -------------------------
$blue: ${theme.v1.colors.textBlue};
$red: $red-base;
$yellow: ${theme.v1.palette.yellow};
$orange: ${theme.v1.palette.orange};
$purple: ${theme.v1.palette.purple};
$variable: ${theme.v1.colors.textBlue};

$brand-primary: ${theme.v1.palette.brandPrimary};
$brand-success: ${theme.v1.palette.brandSuccess};
$brand-warning: ${theme.v1.palette.brandWarning};
$brand-danger: ${theme.v1.palette.brandDanger};

$query-red: ${theme.v1.palette.queryRed};
$query-green: ${theme.v1.palette.queryGreen};
$query-purple: ${theme.v1.palette.queryPurple};
$query-orange: ${theme.v1.palette.orange};

// Status colors
// -------------------------
$online: ${theme.colors.success.text};
$warn: ${theme.colors.warning.text};
$critical: ${theme.colors.error.text};

// Scaffolding
// -------------------------
$body-bg: ${theme.v1.colors.bodyBg};
$page-bg: ${theme.v1.colors.bodyBg};
$dashboard-bg: ${theme.v1.colors.dashboardBg};

$text-color: ${theme.v1.colors.text};
$text-color-strong: ${theme.v1.colors.textStrong};
$text-color-semi-weak: ${theme.v1.colors.textSemiWeak};
$text-color-weak: ${theme.v1.colors.textWeak};
$text-color-faint: ${theme.v1.colors.textFaint};
$text-color-emphasis: ${theme.v1.colors.textStrong};
$text-blue: ${theme.v1.colors.textBlue};

$text-shadow-faint: none;

// gradients
$brand-gradient-horizontal: linear-gradient(to right, #f05a28 30%, #fbca0a 99%);
$brand-gradient-vertical: linear-gradient(#f05a28 30%, #fbca0a 99%);

// Links
// -------------------------
$link-color: ${theme.v1.colors.link};
$link-color-disabled: ${theme.v1.colors.linkDisabled};
$link-hover-color: ${theme.v1.colors.linkHover};
$external-link-color: ${theme.v1.colors.linkExternal};

// Typography
// -------------------------
$headings-color: ${theme.v1.colors.textHeading};
$abbr-border-color: $gray-2 !default;
$text-muted: $text-color-weak;

$hr-border-color: $gray-4 !default;

// Panel
// -------------------------
$panel-bg: ${theme.components.panel.background};
$panel-border: 1px solid ${theme.components.panel.borderColor};
$panel-header-hover-bg: ${theme.colors.action.hover};
$panel-box-shadow: ${theme.components.panel.boxShadow};
$panel-corner: $panel-bg;

// Page header
$page-header-bg: ${theme.v1.colors.pageHeaderBg};
$page-header-shadow: inset 0px -3px 10px $gray-6;
$page-header-border-color: ${theme.v1.colors.pageHeaderBorder};

$divider-border-color: $gray-2;

// Graphite Target Editor
$tight-form-func-bg: ${theme.v1.colors.bg2};
$tight-form-func-highlight-bg: ${styleMixins.hoverColor(theme.v1.colors.bg2, theme.v1)};

$modal-backdrop-bg: ${theme.v1.colors.bg1};
$code-tag-bg: $gray-6;
$code-tag-border: $gray-4;

// cards
$card-background: ${theme.colors.background.secondary};
$card-background-hover: ${theme.colors.background.secondary};
$card-shadow: none;

// Lists
$list-item-bg: $gray-7;
$list-item-hover-bg: $gray-6;
$list-item-shadow: $card-shadow;

$empty-list-cta-bg: $gray-6;

// Scrollbars
$scrollbarBackground: $gray-4;
$scrollbarBackground2: $gray-4;
$scrollbarBorder: $gray-7;

// Tables
// -------------------------
$table-bg-accent: ${theme.colors.background.secondary};
$table-border: ${theme.colors.border.medium};
$table-bg-odd: ${theme.colors.emphasize(theme.colors.background.primary, 0.02)};
$table-bg-hover: ${theme.colors.emphasize(theme.colors.background.primary, 0.05)};

// Buttons
// -------------------------
$btn-secondary-bg: $gray-5;
$btn-secondary-bg-hl: $gray-4;

$btn-primary-bg: $blue-base;
$btn-primary-bg-hl: $blue-shade;

$btn-success-bg: $green-base;
$btn-success-bg-hl: $green-shade;

$btn-danger-bg: $red-base;
$btn-danger-bg-hl: $red-shade;

$btn-inverse-bg: $gray-5;
$btn-inverse-bg-hl: $gray-4;
$btn-inverse-text-color: $gray-1;
$btn-inverse-text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4);

$btn-link-color: $gray-1;

$iconContainerBackground: $white;

$btn-divider-left: $gray-4;
$btn-divider-right: $gray-7;

$btn-drag-image: '../img/grab_light.svg';

$navbar-btn-gicon-brightness: brightness(1.5);

$btn-active-box-shadow: 0px 0px 4px rgba(234, 161, 51, 0.6);

// Forms
// -------------------------
$input-bg: ${theme.components.input.background};
$input-bg-disabled: ${theme.colors.action.disabledBackground};

$input-color: ${theme.components.input.text};
$input-border-color: ${theme.components.input.borderColor};
$input-box-shadow: none;
$input-border-focus: ${theme.v1.palette.blue95};
$input-box-shadow-focus: ${theme.v1.palette.blue95};
$input-color-placeholder: ${theme.v1.colors.formInputPlaceholderText};
$input-label-bg: ${theme.v1.colors.bg2};
$input-color-select-arrow: ${theme.v1.palette.gray60};

// search
$search-shadow: 0 1px 5px 0 $gray-5;

// Typeahead
$typeahead-shadow: 0 5px 10px 0 $gray-5;
$typeahead-selected-bg: $gray-6;
$typeahead-selected-color: $yellow;

// Dropdowns
// -------------------------
$dropdownBackground: ${theme.colors.background.primary};
$dropdownBorder: ${theme.colors.border.weak};
$dropdownDividerTop: ${theme.colors.border.weak};
$dropdownDividerBottom: ${theme.colors.border.weak};
$dropdownShadow: ${theme.shadows.z3};

$dropdownLinkColor: $dark-2;
$dropdownLinkColorHover: $link-color;
$dropdownLinkColorActive: $link-color;

$dropdownLinkBackgroundHover: $gray-6;

// Horizontal forms & lists
// -------------------------
$horizontalComponentOffset: 180px;

// Navbar
// -------------------------
$navbarHeight: 52px;
$navbarBorder: 1px solid $gray-5;

// Sidemenu
// -------------------------
$side-menu-bg: ${theme.v1.palette.gray15};
$side-menu-border: 1px solid ${theme.v1.palette.gray25};
$side-menu-bg-mobile: rgba(0, 0, 0, 0); //$gray-6;
$side-menu-item-hover-bg: ${theme.v1.palette.gray25};
$side-menu-shadow: 5px 0px 10px -5px $gray-1;
$side-menu-link-color: $gray-4;
$side-menu-icon-color: ${theme.v1.palette.gray70};
$side-menu-header-color: ${theme.v1.palette.gray95};

// Menu dropdowns
// -------------------------
$menu-dropdown-bg: ${theme.colors.background.primary};
$menu-dropdown-hover-bg: ${theme.colors.action.hover};
$menu-dropdown-shadow: ${theme.shadows.z3};

// Tabs
// -------------------------
$tab-border-color: $gray-5;

// Form states and alerts
// -------------------------
$warning-text-color: ${theme.colors.warning.text};
$error-text-color: ${theme.colors.error.text};
$success-text-color: ${theme.colors.success.text};

$alert-error-bg: ${theme.colors.error.main};
$alert-success-bg: ${theme.colors.success.main};
$alert-warning-bg: ${theme.colors.warning.main};
$alert-info-bg: ${theme.colors.warning.main};

// Tooltips and popovers
$tooltipBackground: ${theme.components.tooltip.background};
$tooltipColor: ${theme.components.tooltip.text};
$tooltipArrowColor: ${theme.components.tooltip.background};
$tooltipBackgroundError: ${theme.colors.error.main};
$tooltipShadow: ${theme.shadows.z2};

$popover-bg: ${theme.colors.background.primary};
$popover-color: ${theme.colors.text.primary};
$popover-border-color: ${theme.colors.border.weak};
$popover-header-bg: ${theme.colors.background.secondary};
$popover-shadow: ${theme.shadows.z3};

$graph-tooltip-bg: $gray-5;

$tooltipArrowWidth: 5px;
$tooltipLinkColor: lighten($tooltipColor, 5%);

$popover-error-bg: $btn-danger-bg;
$popover-help-bg: $tooltipBackground;
$popover-help-color: $tooltipColor;

$popover-code-bg: ${theme.v1.colors.bg1};
$popover-code-boxshadow: 0 0 5px $gray60;

// images
$checkboxImageUrl: '../img/checkbox_white.png';

// info box
$info-box-border-color: $blue-base;

// footer
$footer-link-color: $gray-3;
$footer-link-hover: $dark-2;

// json explorer
$json-explorer-default-color: black;
$json-explorer-string-color: green;
$json-explorer-number-color: $blue-base;
$json-explorer-boolean-color: $red-base;
$json-explorer-null-color: #855a00;
$json-explorer-undefined-color: rgb(202, 11, 105);
$json-explorer-function-color: #ff20ed;
$json-explorer-rotate-time: 100ms;
$json-explorer-toggler-opacity: 0.6;
$json-explorer-bracket-color: $blue-base;
$json-explorer-key-color: #00008b;
$json-explorer-url-color: $blue-base;

// Changelog and diff
// -------------------------
$diff-label-bg: ${theme.v1.colors.bg3};
$diff-label-fg: $gray-2;

$diff-arrow-color: $dark-2;
$diff-group-bg: ${theme.v1.colors.bg2};

$diff-json-bg: ${theme.v1.colors.bg2};
$diff-json-fg: ${theme.v1.colors.text};

$diff-json-added: $blue-shade;
$diff-json-deleted: $red-shade;

$diff-json-old: #5a372a;
$diff-json-new: #664e33;

$diff-json-changed-fg: $gray-7;
$diff-json-changed-num: $gray-4;

$diff-json-icon: $gray-4;

//Submenu
$variable-option-bg: $dropdownLinkBackgroundHover;

//Switch Slider
// -------------------------
$switch-bg: $white;
$switch-slider-color: $gray-7;
$switch-slider-off-bg: $gray-5;
$switch-slider-on-bg: ${theme.v1.palette.blue77};
$switch-slider-shadow: 0 0 3px $dark-2;

//Checkbox
// -------------------------
$checkbox-bg: $gray-6;
$checkbox-border: 1px solid $gray-3;
$checkbox-checked-bg: linear-gradient(0deg, #ff9830, #e55400);
$checkbox-color: $gray-7;

//Panel Edit
// -------------------------
$panel-editor-shadow: 0px 0px 8px $gray-3;
$panel-editor-side-menu-shadow: drop-shadow(0 0 2px $gray-3);
$panel-editor-viz-item-shadow: 0 0 4px $gray-3;
$panel-editor-viz-item-border: 1px solid $gray-3;
$panel-editor-viz-item-shadow-hover: 0 0 4px $blue-light;
$panel-editor-viz-item-border-hover: 1px solid $blue-light;
$panel-editor-viz-item-bg: $card-background;
$panel-editor-tabs-line-color: $dark-2;

$panel-editor-viz-item-bg-hover: lighten($blue-base, 45%);

$panel-grid-placeholder-bg: lighten(${theme.v1.palette.blue95}, 30%);
$panel-grid-placeholder-shadow: 0 0 4px ${theme.v1.palette.blue95};

// logs
$logs-color-unknown: $gray-5;

// toggle-group
$button-toggle-group-btn-active-bg: $brand-primary;
$button-toggle-group-btn-active-shadow: inset 0 0 4px $white;
$button-toggle-group-btn-separator-border: 1px solid $gray-6;

$vertical-resize-handle-bg: $gray-4;
$vertical-resize-handle-dots: $gray-3;
$vertical-resize-handle-dots-hover: $gray-2;

// Calendar
$calendar-bg-days: $white;
$calendar-bg-now: $gray-6;

`;


text:
using System;
using System.IO;
using System.Collections;

namespace NAudio.SoundFont
{

	/// <summary>
	/// base class for structures that can read themselves
	/// </summary>
	internal abstract class StructureBuilder
	{
		protected ArrayList data;

		public StructureBuilder()
		{
			Reset();
		}

		public abstract object Read(BinaryReader br);
		public abstract void Write(BinaryWriter bw,object o);
		public abstract int Length { get; }

		public void Reset()
		{
			data = new ArrayList();
		}

		public object[] Data
		{
			get
			{
				return data.ToArray();
			}
		}
	}

}


text:
package org.apache.flink.hadoopcompatibility.mapred.record.datatypes;

import java.io.IOException;

import org.apache.flink.core.memory.DataInputView;
import org.apache.flink.core.memory.DataOutputView;
import org.apache.flink.types.Value;
import org.apache.flink.util.InstantiationUtil;
import org.apache.hadoop.io.Writable;

public class WritableWrapper<T extends Writable> implements Value {
	private static final long serialVersionUID = 2L;

	private T wrapped;
	private String wrappedType;
	private ClassLoader cl;

	public WritableWrapper() {
	}

	public WritableWrapper(T toWrap) {
		wrapped = toWrap;
		wrappedType = toWrap.getClass().getCanonicalName();
	}

	public T value() {
		return wrapped;
	}

	@Override
	public void write(DataOutputView out) throws IOException {
		out.writeUTF(wrappedType);
		wrapped.write(out);
	}

	@Override
	public void read(DataInputView in) throws IOException {
		if(cl == null) {
			cl = Thread.currentThread().getContextClassLoader();
		}
		wrappedType = in.readUTF();
		try {
			@SuppressWarnings("unchecked")
			Class<T> wrClass = (Class<T>) Class.forName(wrappedType, true, cl).asSubclass(Writable.class);
			wrapped = InstantiationUtil.instantiate(wrClass, Writable.class);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException("Error creating the WritableWrapper", e);
		}
		wrapped.readFields(in);
	}

}


text:
layout: post
title: "Two Party System"
excerpt: "If you vote for a third party, you're throwing away your vote!"
author: jroelofs
tags: [voting]
comments: false
image:
  feature:
  credit: Jon Roelofs
  creditlink: /authors
---

A variation on the False Dilemma fallacy wherein it is assumed that the only alternative to a political party you're not interested in is an equally bad other choice.

> "Nobody for President 2016!"


text:
namespace TileManager {
     class NotifyEvent : EventArgs {

         public string Text { get; private set; }

         public NotifyEvent(string text) {
             this.Text = text;
         }

         public string ControlName { get; private set; }
         public string Property { get; private set; }
         public string Value { get; private set; }

         public NotifyEvent(string controlName, string property, string value) {
             this.ControlName = controlName;
             this.Property = property;
             this.Value = value;
         }
     }
}


text:
package scaladci
package examples
import org.specs2.mutable.Specification

/*
Shopping cart example (version 3) - removing shop role

Delegated most of the earlier shop role responsibilities to the customer role. We can
view the shop as the system as a whole (maybe a context?) that we operate in - or the
system that responds to various customer actions rather than a Role on its own.

Now distinguishing between System and UI as different kinds of responses to the user
inputs. When an item is checked with the warehouse we could see it as the System doing
some technical background checks that doesn't involve the customer whereas the UI is
what communicates with the customer.

"Internally" the systems regards the cart as an Order. The customer primarily thinks
"shopping cart" and might change to a mental model of "order" once he's about to pay?

See discussion at:
https://groups.google.com/forum/?fromgroups=#!topic/object-composition/JJiLWBsZWu0

===========================================================================
USE CASE:	Place Order [user-goal]

Person browsing around finds product(s) in a web shop that he/she wants to buy.

Primary actor.. Web customer ("Customer")
Scope.......... Web shop ("Shop")
Preconditions.. Shop presents product(s) to Customer
Trigger........ Customer wants to buy certain product(s)

Main Success Scenario
---------------------------------------------------------------------------
1. Customer marks Desired Product in Shop
    - System reserves Product in Warehouse
    - System adds Item to Order (can repeat from step 1)
    - UI shows updated contents of Cart to Customer
2. Customer requests to review Order
    - UI presents Cart with Items and prices to Customer
3. Customer pays Order
    - System confirms sufficient funds are available
    - System initiates transfer of funds
    - System informs Warehouse to ship Products
    - UI confirms purchase to Customer

Deviations
---------------------------------------------------------------------------
1a. Product is out of stock:
    1. UI informs Customer that Product is out of stock.

1b. Customer has gold membership:
    1. System adds discounted Product to Order.

3a. Customer has insufficient funds to pay Order:
    1. UI informs Customer of insufficient funds.
        a. Customer removes unaffordable Item(s) from Cart:
            1. Go to step 3.
===========================================================================
*/

class ShoppingCart3 extends Specification {
  import ShoppingCartModel._

  {
    @context
    class PlaceOrder(shop: Company, customer: Person) {

      // UC steps
      def customerMarksdesiredProductInshop(productId: Int): Option[Product] = {
        desiredProductId = productId
        customer.markdesiredProductInshop
      }
      def customerRequestsToReviewOrder: Seq[(Int, Product)] =
        customer.reviewOrder
      def customerPaysOrder: Boolean =
        customer.payOrder

      // Deviation(s)
      def customerRemovesProductFromcart(productId: Int): Option[Product] =
        customer.removeProductFromcart(productId)

      // Context data house-keeping
      private var desiredProductId: Int = _

      // Roles
      private val warehouse = shop
      private val cart      = Order(customer)

      role customer {
        def markdesiredProductInshop: Option[Product] = {
          if (!warehouse.hasdesiredProduct)
            return None
          val product = warehouse.reservedesiredProduct
          val discountedPrice = customer.getMemberPriceOf(product)
          val desiredProduct = product.copy(price = discountedPrice)
          cart.addItem(desiredProductId, desiredProduct)
          Some(desiredProduct)
        }
        def reviewOrder = cart.getItems
        def removeProductFromcart(productId: Int) = cart.removeItem(productId: Int)
        def payOrder: Boolean = {
          val orderTotal = cart.total
          if (orderTotal > customer.cash)
            return false

          customer.cash -= orderTotal
          shop.cash += orderTotal

          customer.owns ++= cart.items
          cart.items foreach (warehouse.stock remove _._1)
          true
        }

        def getMemberPriceOf(product: Product) = {
          val customerIsGoldMember = shop.goldMembers.contains(customer)
          val goldMemberReduction = 0.5
          val discountFactor = if (customerIsGoldMember) goldMemberReduction else 1
          (product.price * discountFactor).toInt
        }
      }

      role warehouse {
        def hasdesiredProduct = shop.stock.isDefinedAt(desiredProductId)
        def reservedesiredProduct = shop.stock(desiredProductId) // dummy reservation
      }

      role cart {
        def addItem(productId: Int, product: Product) {
          cart.items.put(productId, product)
        }
        def removeItem(productId: Int): Option[Product] = {
          if (!cart.items.isDefinedAt(productId))
            return None
          cart.items.remove(productId)
        }
        def getItems = cart.items.toIndexedSeq.sortBy(_._1)
        def total = cart.items.map(_._2.price).sum
      }
    }

    // Test various scenarios.
    // (copy and paste of ShoppingCart2 tests)

    "Main success scenario" in new ShoppingCart {

      // Initial status (same for all tests...)
      shop.stock === Map(tires, wax, bmw)
      shop.cash === 100000
      customer.cash === 20000
      customer.owns === Map()

      val order = new PlaceOrder(shop, customer)

      // customer wants wax and tires
      order.customerMarksdesiredProductInshop(p1)
      order.customerMarksdesiredProductInshop(p2)

      order.customerRequestsToReviewOrder === Seq(wax, tires)

      val orderCompleted = order.customerPaysOrder === true

      shop.stock === Map(bmw)
      shop.cash === 100000 + 40 + 600
      customer.cash === 20000 - 40 - 600
      customer.owns === Map(tires, wax)
    }

    "Product out of stock" in new ShoppingCart {

      // Wax out of stock
      shop.stock.remove(p1)
      shop.stock === Map(tires, bmw)

      val order = new PlaceOrder(shop, customer)

      // customer wants wax
      val itemAdded = order.customerMarksdesiredProductInshop(p1) === None
      order.customerRequestsToReviewOrder === Seq()

      order.customerMarksdesiredProductInshop(p2)

      val orderCompleted = order.customerPaysOrder === true

      shop.stock === Map(bmw)
      shop.cash === 100000 + 600
      customer.cash === 20000 - 600
      customer.owns === Map(tires)
    }

    "customer has gold membership" in new ShoppingCart {

      // customer is gold member
      shop.goldMembers.add(customer)
      shop.goldMembers.contains(customer) === true

      val order = new PlaceOrder(shop, customer)

      order.customerMarksdesiredProductInshop(p1)

      val discountedWax = 1 -> Product("Wax", (40 * 0.5).toInt)
      order.customerRequestsToReviewOrder === Seq(discountedWax)

      val orderCompleted = order.customerPaysOrder === true

      shop.stock === Map(tires, bmw)
      shop.cash === 100000 + 20
      customer.cash === 20000 - 20
      customer.owns === Map(discountedWax)
    }

    "customer has too low credit" in new ShoppingCart {

      val order = new PlaceOrder(shop, customer)

      // customer wants a BMW
      val itemAdded = order.customerMarksdesiredProductInshop(p3)

      // Any product is added - shop doesn't yet know if customer can afford it
      itemAdded === Some(bmw._2)
      order.customerRequestsToReviewOrder === Seq(bmw)

      // customer tries to pay order
      val paymentStatus = order.customerPaysOrder

      // shop informs customer of too low credit
      paymentStatus === false

      // customer removes unaffordable BMW from cart
      order.customerRemovesProductFromcart(p3)

      // customer aborts shopping and no purchases are made
      shop.stock === Map(tires, wax, bmw)
      shop.cash === 100000
      customer.cash === 20000
      customer.owns === Map()
    }

    "All deviations in play" in new ShoppingCart {

      // Tires out of stock
      shop.stock.remove(p2)
      shop.stock === Map(wax, bmw)

      // We have a gold member
      shop.goldMembers.add(customer)

      val order = new PlaceOrder(shop, customer)

      // Let's get some tires
      val tiresItemAdded = order.customerMarksdesiredProductInshop(p2)

      // Product out of stock!
      shop.stock.contains(p2) === false

      // Nothing added to order yet
      tiresItemAdded === None
      order.customerRequestsToReviewOrder === Seq()

      // Let's buy the BMW instead. As a gold member that should be possible!
      val bmwItemAdded = order.customerMarksdesiredProductInshop(p3)

      // Discounted BMW is added to order
      val discountedBMW = Product("BMW", (50000 * 0.5).toInt)
      bmwItemAdded === Some(discountedBMW)
      order.customerRequestsToReviewOrder === Seq(p3 -> discountedBMW)

      // Ouch! We couldn't afford it.
      val paymentAttempt1 = order.customerPaysOrder === false

      // It's still 5000 too much for us, even with the membership discount
      discountedBMW.price - customer.cash === 5000

      // Ok, no new car today
      order.customerRemovesProductFromcart(p3)

      // Order is back to empty
      order.customerRequestsToReviewOrder === Seq()

      // Let's get some wax anyway...
      val waxItemAdded = order.customerMarksdesiredProductInshop(p1)

      // Did we get our membership discount on this one?
      val discountedWax = Product("Wax", (40 * 0.5).toInt)
      waxItemAdded === Some(discountedWax)

      // Now we can afford it!
      val paymentAttempt2 = order.customerPaysOrder === true

      // Not much shopping done Today. At least we got some cheap wax.
      shop.stock === Map(bmw)
      shop.cash === 100000 + 20
      customer.cash === 20000 - 20
      customer.owns === Map(p1 -> discountedWax)
    }
  }
}


text:
<?php

// require_once 'Zend/Stdlib/CallbackHandler.php';
// require_once 'Zend/Stdlib/SplPriorityQueue.php';

/**
 * Specialized priority queue implementation for use with an intercepting
 * filter chain.
 *
 * Allows removal
 *
 * @category   Zend
 * @package    Zend_EventManager
 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_EventManager_Filter_FilterIterator extends Zend_Stdlib_SplPriorityQueue
{
    /**
     * Does the queue contain a given value?
     *
     * @param  mixed $datum
     * @return bool
     */
    public function contains($datum)
    {
        $chain = clone $this;
        foreach ($chain as $item) {
            if ($item === $datum) {
                return true;
            }
        }
        return false;
    }

    /**
     * Remove a value from the queue
     *
     * This is an expensive operation. It must first iterate through all values,
     * and then re-populate itself. Use only if absolutely necessary.
     *
     * @param  mixed $datum
     * @return bool
     */
    public function remove($datum)
    {
        $this->setExtractFlags(self::EXTR_BOTH);

        // Iterate and remove any matches
        $removed = false;
        $items   = array();
        $this->rewind();
        while (!$this->isEmpty()) {
            $item = $this->extract();
            if ($item['data'] === $datum) {
                $removed = true;
                continue;
            }
            $items[] = $item;
        }

        // Repopulate
        foreach ($items as $item) {
            $this->insert($item['data'], $item['priority']);
        }

        $this->setExtractFlags(self::EXTR_DATA);
        return $removed;
    }

    /**
     * Iterate the next filter in the chain
     *
     * Iterates and calls the next filter in the chain.
     *
     * @param  mixed                                   $context
     * @param  array                                   $params
     * @param  Zend_EventManager_Filter_FilterIterator $chain
     * @return mixed
     */
    #[\ReturnTypeWillChange]
    public function next($context = null, array $params = array(), $chain = null)
    {
        if (empty($context) || $chain->isEmpty()) {
            return;
        }

        $next = $this->extract();
        if (!$next instanceof Zend_Stdlib_CallbackHandler) {
            return;
        }

        $return = call_user_func($next->getCallback(), $context, $params, $chain);
        return $return;
    }
}


text:
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision: 288721 $ -->

<reference xml:id="ref.mysqli">
 <title>Aliases and deprecated Mysqli &Functions;</title>

 &reference.mysqli.entities.functions;
</reference>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->


text:
package org.radargun.service;

import org.infinispan.remoting.transport.Transport;

/**
 * Infinispan 10.0.x Life cycle
 *
 * @author Diego Lovison <dlovison@redhat.com>
 */
public class Infinispan100Lifecycle extends Infinispan90Lifecycle {

   public Infinispan100Lifecycle(Infinispan100EmbeddedService service) {
      super(service);
   }

   public Transport createTransport() {
      return new Infinispan100HookedJGroupsTransport(this);
   }
}


text:
// Copyright 2018 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 "components/subresource_filter/content/browser/ruleset_publisher_impl.h"

#include <stddef.h>

#include <string>
#include <tuple>
#include <utility>

#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/numerics/safe_conversions.h"
#include "base/run_loop.h"
#include "base/task/task_runner.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/prefs/testing_pref_service.h"
#include "components/subresource_filter/content/browser/ruleset_service.h"
#include "components/subresource_filter/core/common/test_ruleset_creator.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "ipc/ipc_platform_file.h"
#include "ipc/ipc_test_sink.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace subresource_filter {

namespace {

using MockClosureTarget =
    ::testing::StrictMock<::testing::MockFunction<void()>>;

class NotifyingMockRenderProcessHost : public content::MockRenderProcessHost {
 public:
  explicit NotifyingMockRenderProcessHost(
      content::BrowserContext* browser_context,
      content::RenderProcessHostCreationObserver* observer)
      : content::MockRenderProcessHost(browser_context) {
    if (observer)
      observer->OnRenderProcessHostCreated(this);
  }
};

std::string ReadFileContentsToString(base::File* file) {
  size_t length = base::checked_cast<size_t>(file->GetLength());
  std::string contents(length, 0);
  file->Read(0, &contents[0], base::checked_cast<int>(length));
  return contents;
}

}  // namespace

class SubresourceFilterRulesetPublisherImplTest : public ::testing::Test {
 public:
  SubresourceFilterRulesetPublisherImplTest()
      : existing_renderer_(&browser_context_, nullptr) {}

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

 protected:
  void SetUp() override { ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); }

  void TearDown() override {}

  content::TestBrowserContext* browser_context() { return &browser_context_; }

  base::FilePath temp_dir() const { return scoped_temp_dir_.GetPath(); }

  base::FilePath scoped_temp_file() const {
    return scoped_temp_dir_.GetPath().AppendASCII("data");
  }

  void AssertSetRulesetFileWithContent(base::File* ruleset_file,
                                       const std::string& expected_contents) {
    ASSERT_TRUE(ruleset_file);
    ASSERT_TRUE(ruleset_file->IsValid());
    ASSERT_EQ(expected_contents, ReadFileContentsToString(ruleset_file));
  }

 private:
  base::ScopedTempDir scoped_temp_dir_;
  content::BrowserTaskEnvironment task_environment_;
  content::TestBrowserContext browser_context_;
  NotifyingMockRenderProcessHost existing_renderer_;
};

class MockRulesetPublisherImpl : public RulesetPublisherImpl {
 public:
  template <typename... Args>
  explicit MockRulesetPublisherImpl(Args&&... args)
      : RulesetPublisherImpl(std::forward<Args>(args)...) {}
  void SendRulesetToRenderProcess(
      base::File* file,
      content::RenderProcessHost* process) override {
    last_file_[process] = file;
    sent_count_++;
  }

  size_t RulesetSent() const { return sent_count_; }

  base::File* RulesetFileForProcess(content::RenderProcessHost* process) {
    auto it = last_file_.find(process);
    if (it == last_file_.end())
      return nullptr;
    return it->second;
  }

 private:
  size_t sent_count_ = 0;
  std::map<content::RenderProcessHost*, base::File*> last_file_;
};

TEST_F(SubresourceFilterRulesetPublisherImplTest, NoRuleset_NoIPCMessages) {
  NotifyingMockRenderProcessHost existing_renderer(browser_context(), nullptr);
  MockRulesetPublisherImpl service(nullptr,
                                   base::ThreadTaskRunnerHandle::Get());
  NotifyingMockRenderProcessHost new_renderer(browser_context(), &service);
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(0u, service.RulesetSent());
}

TEST_F(SubresourceFilterRulesetPublisherImplTest,
       PublishedRuleset_IsDistributedToExistingAndNewRenderers) {
  const char kTestFileContents[] = "foobar";
  base::WriteFile(scoped_temp_file(), kTestFileContents,
                  strlen(kTestFileContents));

  RulesetFilePtr file(
      new base::File(scoped_temp_file(),
                     base::File::FLAG_OPEN | base::File::FLAG_READ),
      base::OnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get()));

  NotifyingMockRenderProcessHost existing_renderer(browser_context(), nullptr);
  MockClosureTarget publish_callback_target;
  MockRulesetPublisherImpl service(nullptr,
                                   base::ThreadTaskRunnerHandle::Get());
  service.SetRulesetPublishedCallbackForTesting(base::BindOnce(
      &MockClosureTarget::Call, base::Unretained(&publish_callback_target)));

  EXPECT_CALL(publish_callback_target, Call()).Times(1);
  service.PublishNewRulesetVersion(std::move(file));
  base::RunLoop().RunUntilIdle();
  ::testing::Mock::VerifyAndClearExpectations(&publish_callback_target);

  ASSERT_EQ(2u, service.RulesetSent());
  ASSERT_NO_FATAL_FAILURE(AssertSetRulesetFileWithContent(
      service.RulesetFileForProcess(&existing_renderer), kTestFileContents));

  NotifyingMockRenderProcessHost second_renderer(browser_context(), &service);
  base::RunLoop().RunUntilIdle();

  ASSERT_EQ(3u, service.RulesetSent());
  ASSERT_NO_FATAL_FAILURE(AssertSetRulesetFileWithContent(
      service.RulesetFileForProcess(&second_renderer), kTestFileContents));
}

TEST_F(SubresourceFilterRulesetPublisherImplTest,
       PublishesRulesetInOnePostTask) {
  // Regression test for crbug.com/817308. Test verifies that ruleset is
  // published on browser startup via exactly one PostTask.

  const base::FilePath base_dir =
      temp_dir().AppendASCII("Rules").AppendASCII("Indexed");

  // Create a testing ruleset.
  testing::TestRulesetPair ruleset;
  ASSERT_NO_FATAL_FAILURE(
      testing::TestRulesetCreator().CreateRulesetToDisallowURLsWithPathSuffix(
          "foo", &ruleset));

  // Create local state and save the ruleset version to emulate invariant that
  // the version of the last indexed ruleset is stored in the local state.
  TestingPrefServiceSimple prefs;
  IndexedRulesetVersion::RegisterPrefs(prefs.registry());
  IndexedRulesetVersion current_version(
      "1.2.3.4", IndexedRulesetVersion::CurrentFormatVersion());
  current_version.SaveToPrefs(&prefs);

  // Create ruleset data on a disk.
  const base::FilePath version_dir_path =
      IndexedRulesetLocator::GetSubdirectoryPathForVersion(base_dir,
                                                           current_version);
  ASSERT_EQ(RulesetService::IndexAndWriteRulesetResult::SUCCESS,
            RulesetService::WriteRuleset(version_dir_path,
                                         /* license_path =*/base::FilePath(),
                                         ruleset.indexed.contents.data(),
                                         ruleset.indexed.contents.size()));

  // Create a ruleset service and its harness.
  scoped_refptr<base::TestSimpleTaskRunner> blocking_task_runner =
      base::MakeRefCounted<base::TestSimpleTaskRunner>();
  scoped_refptr<base::TestSimpleTaskRunner> background_task_runner =
      base::MakeRefCounted<base::TestSimpleTaskRunner>();
  NotifyingMockRenderProcessHost renderer_host(browser_context(), nullptr);
  base::RunLoop callback_waiter;
  auto content_service =
      std::make_unique<MockRulesetPublisherImpl>(nullptr, blocking_task_runner);
  content_service->SetRulesetPublishedCallbackForTesting(
      callback_waiter.QuitClosure());
  auto* mock_publisher = content_service.get();

  // |RulesetService| constructor should read the last indexed ruleset version
  // and post ruleset setup on |blocking_task_runner|.
  ASSERT_EQ(0u, blocking_task_runner->NumPendingTasks());
  auto service =
      std::make_unique<RulesetService>(&prefs, background_task_runner, base_dir,
                                       nullptr, std::move(content_service));

  // The key test assertion is that ruleset data is published via exactly one
  // post task on |blocking_task_runner|. It is important to run pending tasks
  // only once here.
  ASSERT_EQ(1u, blocking_task_runner->NumPendingTasks());
  blocking_task_runner->RunPendingTasks();
  callback_waiter.Run();

  // Check that the ruleset data is delivered to the renderer.
  ASSERT_EQ(2u, mock_publisher->RulesetSent());
  const std::string expected_data(ruleset.indexed.contents.begin(),
                                  ruleset.indexed.contents.end());
  ASSERT_NO_FATAL_FAILURE(AssertSetRulesetFileWithContent(
      mock_publisher->RulesetFileForProcess(&renderer_host), expected_data));

  //
  // |RulesetPublisherImpl| destruction requires additional tricks. Its member
  // |VerifiedRulesetDealer::Handle| posts task upon destruction on
  // |blocking_task_runner|.
  service.reset();
  // Need to wait for |VerifiedRulesetDealer| destruction on the
  // |blocking_task_runner|.
  blocking_task_runner->RunPendingTasks();
}

}  // namespace subresource_filter