text:
package com.vaadin.client.ui.datefield;

import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.vaadin.client.LocaleNotLoadedException;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.AbstractFieldConnector;
import com.vaadin.client.ui.VAbstractCalendarPanel;
import com.vaadin.client.ui.VAbstractPopupCalendar;
import com.vaadin.client.ui.VDateField;
import com.vaadin.shared.ui.datefield.AbstractDateFieldServerRpc;
import com.vaadin.shared.ui.datefield.AbstractDateFieldState;
import com.vaadin.shared.ui.datefield.AbstractDateFieldState.AccessibleElement;

public abstract class AbstractDateFieldConnector<R extends Enum<R>>
        extends AbstractFieldConnector {

    @Override
    protected void init() {
        super.init();
        getWidget().rpc = getRpcProxy(AbstractDateFieldServerRpc.class);
    }

    private void updateResolution() {
        VDateField<R> widget = getWidget();
        Map<String, Integer> stateResolutions = getState().resolutions;
        Optional<R> newResolution = widget.getResolutions()
                .filter(res -> stateResolutions.containsKey(res.name()))
                .findFirst();

        widget.setCurrentResolution(newResolution.orElse(null));
    }

    private Map<R, Integer> getTimeValues() {
        VDateField<R> widget = getWidget();
        Map<String, Integer> stateResolutions = getState().resolutions;
        Stream<R> resolutions = widget.getResolutions();
        R resolution = widget.getCurrentResolution();
        return resolutions.collect(Collectors.toMap(Function.identity(),
                res -> resolution.compareTo(res) <= 0
                        ? stateResolutions.get(res.name())
                        : null));
    }

    /**
     * Returns the default date (when no date is selected) components as a map
     * from Resolution to the corresponding value.
     *
     * @return default date component map
     * @since 8.2
     */
    protected Map<R, Integer> getDefaultValues() {
        VDateField<R> widget = getWidget();
        Map<String, Integer> stateResolutions = getState().resolutions;
        Stream<R> resolutions = widget.getResolutions();
        R resolution = widget.getCurrentResolution();
        return resolutions.collect(Collectors.toMap(Function.identity(),
                res -> resolution.compareTo(res) <= 0
                        ? stateResolutions.get("default-" + res.name())
                        : null));
    }

    @SuppressWarnings("unchecked")
    @Override
    public VDateField<R> getWidget() {
        return (VDateField<R>) super.getWidget();
    }

    @Override
    public AbstractDateFieldState getState() {
        return (AbstractDateFieldState) super.getState();
    }

    @Override
    public void onStateChanged(StateChangeEvent stateChangeEvent) {
        super.onStateChanged(stateChangeEvent);
        VDateField<R> widget = getWidget();

        // Save details
        widget.client = getConnection();
        widget.connector = this;

        widget.setReadonly(isReadOnly());
        widget.setEnabled(isEnabled());

        final String locale = getState().locale;
        try {
            widget.dts.setLocale(locale);
            widget.setCurrentLocale(locale);
        } catch (final LocaleNotLoadedException e) {
            widget.setCurrentLocale(widget.dts.getLocale());
            getLogger().severe("Tried to use an unloaded locale \"" + locale
                    + "\". Using default locale (" + widget.getCurrentLocale()
                    + ").");
            getLogger().log(Level.SEVERE,
                    e.getMessage() == null ? "" : e.getMessage(), e);
        }

        // We show week numbers only if the week starts with Monday, as ISO 8601
        // specifies
        widget.setShowISOWeekNumbers(getState().showISOWeekNumbers
                && widget.dts.getFirstDayOfWeek() == 1);

        // Remove old stylename that indicates current resolution
        setWidgetStyleName(widget.getStylePrimaryName() + "-"
                + widget.resolutionAsString(), false);

        updateResolution();

        // Add stylename that indicates current resolution
        setWidgetStyleName(widget.getStylePrimaryName() + "-"
                + widget.resolutionAsString(), true);

        widget.setCurrentDate(getTimeValues());
        widget.setDefaultDate(getDefaultValues());
    }

    @OnStateChange("assistiveLabels")
    private void updateAssistiveLabels() {
        if (getWidget() instanceof VAbstractPopupCalendar) {
            setAndUpdateAssistiveLabels(
                    ((VAbstractPopupCalendar) getWidget()).calendar);
        }
    }

    /**
     * Sets assistive labels for the calendar panel's navigation elements, and
     * updates these labels.
     *
     * @param calendar
     *         the calendar panel for which to set the assistive labels
     * @since
     */
    protected void setAndUpdateAssistiveLabels(
            VAbstractCalendarPanel calendar) {
        calendar.setAssistiveLabelPreviousMonth(
                getState().assistiveLabels.get(AccessibleElement.PREVIOUS_MONTH));
        calendar.setAssistiveLabelNextMonth(
                getState().assistiveLabels.get(AccessibleElement.NEXT_MONTH));
        calendar.setAssistiveLabelPreviousYear(
                getState().assistiveLabels.get(AccessibleElement.PREVIOUS_YEAR));
        calendar.setAssistiveLabelNextYear(
                getState().assistiveLabels.get(AccessibleElement.NEXT_YEAR));

        calendar.updateAssistiveLabels();
    }

    @Override
    public void flush() {
        super.flush();
        getWidget().updateBufferedValues();
        getWidget().sendBufferedValues();
    }

    private static Logger getLogger() {
        return Logger.getLogger(AbstractDateFieldConnector.class.getName());
    }
}


text:
package com.dxy.android.statistics.util;

import android.text.TextUtils;

import org.apache.commons.codec.binary.Base64;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StreamCorruptedException;

public class Base64Util {

	/** BASE64 加密数据 */
	public static String encodeBASE64(String s) {
		if (s == null)
			return null;
		try {
			return new String(Base64.encodeBase64(s.getBytes()));
		} catch (Exception e) {
			return null;
		}
	}

	public static String encodeBASE64(byte[] s) {
		if (s == null)
			return null;
		try {
			return new String(Base64.encodeBase64(s));
		} catch (Exception e) {
			return null;
		}
	}

	/** BASE64 解密数据 */
	public static byte[] decodeBASE64(String s) {
		if (s == null)
			return null;
		try {
			return Base64.decodeBase64(s.getBytes());
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 将对象转换成byte数组,并将其进行base64编码
	 */
	public static String objectToString(Object obj) {
		String str = "";
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(baos);
			oos.writeObject(obj);
			str = Base64Util.encodeBASE64((baos.toByteArray()));
		} catch (IOException e) {
			e.printStackTrace();
		}
		return str;
	}

	/**
	 * 对Base64格式的字符串进行解码
	 */
	public static Object stringToObject(String str) {
		Object obj = null;
		try {
			if (!TextUtils.isEmpty(str)) {
				byte[] base64Bytes = Base64Util.decodeBASE64(str);
				ByteArrayInputStream bais = new ByteArrayInputStream(base64Bytes);
				ObjectInputStream ois = new ObjectInputStream(bais);
				obj = ois.readObject();
			}
		} catch (StreamCorruptedException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return obj;
	}

}

redpajama_set_name: RedPajamaGithub


text:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/menu_epsorting"
        android:icon="@drawable/ic_action_sort"
        app:showAsAction="never"
        android:title="@string/sort"/>

</menu>


text:
title: 2017-8-10 string 去掉标点符号
date: 2017-8-10
categories: [Notes, Python]
tags: [Python, string]
---
### Python 字符串去掉标点符号最佳实践

#### 方法一:
> `str.isalnum`:
> S.isalnum() -> bool
> Return True if all characters in S are alphanumeric and there is at least one character in S, False otherwise.

```python
>>> string = "Special $#! characters   spaces 888323"
>>> ''.join(e for e in string if e.isalnum())
'Specialcharactersspaces888323'
```
> 特点:
> - 只能识别字母和数字,杀伤力大,会把中文、空格之类的也干掉
<!--more-->

#### 方法二:
> string.punctuation

```python
import re, string

s ="string. With. Punctuation?" # Sample string

# 写法一:
out = s.translate(string.maketrans("",""), string.punctuation)

# 写法二:
out = s.translate(None, string.punctuation)

# 写法三:
exclude = set(string.punctuation)
out = ''.join(ch for ch in s if ch not in exclude)

# 写法四:
>>> for c in string.punctuation:
			s = s.replace(c,"")
>>> s
'string With Punctuation'

# 写法五:
out = re.sub('[%s]' % re.escape(string.punctuation), '', s)
## re.escape:对字符串中所有可能被解释为正则运算符的字符进行转义

# 写法六:
# string.punctuation 只包括 ascii 格式; 想要一个包含更广(但是更慢)的方法是使用: unicodedata module :
from unicodedata import category
s = u'String — with - «Punctuation »...'
out = re.sub('[%s]' % re.escape(string.punctuation), '', s)
print 'Stripped', out
# 输出:u'Stripped String \u2014 with  \xabPunctuation \xbb'
out = ''.join(ch for ch in s if category(ch)[0] != 'P')
print 'Stripped', out
# 输出:u'Stripped String  with  Punctuation '

# For Python 3 str or Python 2 unicode values, str.translate() only takes a dictionary; codepoints (integers) are looked up in that mapping and anything mapped to None is removed.
# To remove (some?) punctuation then, use:
import string
remove_punct_map = dict.fromkeys(map(ord, string.punctuation))
s.translate(remove_punct_map)

# Your method doesn't work in Python 3, as the translate method doesn't accept the second argument any more.
import unicodedata
import sys
tbl = dict.fromkeys(i for i in range(sys.maxunicode) if unicodedata.category(chr(i)).startswith('P'))
def remove_punctuation(text):
	return text.translate(tbl)
```

#### 方法三:
> re

```python
import re
s ="string. With. Punctuation?"
s = re.sub(r'[^\w\s]','',s)
```

### 测时:
```python
import re, string, timeit

s ="string. With. Punctuation"

exclude = set(string.punctuation)
table = string.maketrans("","")
regex = re.compile('[%s]' % re.escape(string.punctuation))

def test_set(s):
	return ''.join(ch for ch in s if ch not in exclude)

def test_re(s):
	return regex.sub('', s)

def test_trans(s):
	return s.translate(table, string.punctuation)

def test_repl(s):
	for c in string.punctuation:
		s=s.replace(c,"")
	return s

print"sets :",timeit.Timer('f(s)', 'from __main__ import s,test_set as f').timeit(1000000)
print"regex :",timeit.Timer('f(s)', 'from __main__ import s,test_re as f').timeit(1000000)
print"translate :",timeit.Timer('f(s)', 'from __main__ import s,test_trans as f').timeit(1000000)
print"replace :",timeit.Timer('f(s)', 'from __main__ import s,test_repl as f').timeit(1000000)

out_put:
# sets : 19.8566138744
# regex : 6.86155414581
# translate : 2.12455511093
# replace : 28.4436721802
```


text:
ACCEPTED

#### According to
International Plant Names Index

#### Published in
null

#### Original name
Pieris doyonensis Hand.-Mazz.

### Remarks
null


text:
package org.apache.shardingsphere.elasticjob.lite.spring.boot.job.fixture.job.impl;

import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.elasticjob.api.ShardingContext;
import org.apache.shardingsphere.elasticjob.lite.spring.boot.job.fixture.job.CustomJob;
import org.apache.shardingsphere.elasticjob.lite.spring.boot.job.repository.BarRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

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

@Component
@Slf4j
public class CustomTestJob implements CustomJob {

    @Autowired
    private BarRepository barRepository;

    @Override
    public void execute(final ShardingContext shardingContext) {
        int i = shardingContext.getShardingItem();
        List<String> results = new ArrayList<>();
        String data;
        while (null != (data = barRepository.getById(i))) {
            results.add(data);
            i += shardingContext.getShardingTotalCount();
        }
        log.info("{}", results);
    }
}


text:
/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found.  It
 * should not be modified by hand.
 */

package com.deftech.viewtils.test;

public final class R {
    public static final class id {
        public static final int textView = 0x10000;
        public static final int button = 0x10001;
        public static final int layout = 0x10002;
        public static final int spinner = 0x10003;
    }

    public static final class string {
        public static final int tv_str = 0x10100;
        public static final int btn_str = 0x10101;
    }

    public static final class plurals {
    }

    public static final class array {
        public static final int spinner_items = 0x10300;
    }

    public static final class color {
    }

    public static final class drawable {
    }

    public static final class layout {
        public static final int main_layout = 0x10600;
        public static final int spinner_layout = 0x10601;
    }

    public static final class anim {
    }

    public static final class animator {
    }

    public static final class raw {
    }

    public static final class attr {
    }

    public static final class menu {
    }

    public static final class xml {
    }

    public static final class styleable {
    }

    public static final class dimen {
    }

    public static final class integer {
    }

    public static final class bool {
    }

    public static final class style {
    }
}


text:
package internal

import (
	"github.com/hyperledger/fabric-protos-go/peer"
	"github.com/hyperledger/fabric/common/flogging"
	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/privacyenabledstate"
	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil"
	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
)

var logger = flogging.MustGetLogger("valinternal")

// Validator is supposed to validate the transactions based on public data and hashes present in a block
// and returns a batch that should be used to update the state
type Validator interface {
	ValidateAndPrepareBatch(block *Block, doMVCCValidation bool) (*PubAndHashUpdates, error)
}

// Block is used to used to hold the information from its proto format to a structure
// that is more suitable/friendly for validation
type Block struct {
	Num uint64
	Txs []*Transaction
}

// Transaction is used to hold the information from its proto format to a structure
// that is more suitable/friendly for validation
type Transaction struct {
	IndexInBlock            int
	ID                      string
	RWSet                   *rwsetutil.TxRwSet
	ValidationCode          peer.TxValidationCode
	ContainsPostOrderWrites bool
}

// PubAndHashUpdates encapsulates public and hash updates. The intended use of this to hold the updates
// that are to be applied to the statedb  as a result of the block commit
type PubAndHashUpdates struct {
	PubUpdates  *privacyenabledstate.PubUpdateBatch
	HashUpdates *privacyenabledstate.HashedUpdateBatch
}

// NewPubAndHashUpdates constructs an empty PubAndHashUpdates
func NewPubAndHashUpdates() *PubAndHashUpdates {
	return &PubAndHashUpdates{
		privacyenabledstate.NewPubUpdateBatch(),
		privacyenabledstate.NewHashedUpdateBatch(),
	}
}

// ContainsPvtWrites returns true if this transaction is not limited to affecting the public data only
func (t *Transaction) ContainsPvtWrites() bool {
	for _, ns := range t.RWSet.NsRwSets {
		for _, coll := range ns.CollHashedRwSets {
			if coll.PvtRwSetHash != nil {
				return true
			}
		}
	}
	return false
}

// RetrieveHash returns the hash of the private write-set present
// in the public data for a given namespace-collection
func (t *Transaction) RetrieveHash(ns string, coll string) []byte {
	if t.RWSet == nil {
		return nil
	}
	for _, nsData := range t.RWSet.NsRwSets {
		if nsData.NameSpace != ns {
			continue
		}

		for _, collData := range nsData.CollHashedRwSets {
			if collData.CollectionName == coll {
				return collData.PvtRwSetHash
			}
		}
	}
	return nil
}

// ApplyWriteSet adds (or deletes) the key/values present in the write set to the PubAndHashUpdates
func (u *PubAndHashUpdates) ApplyWriteSet(
	txRWSet *rwsetutil.TxRwSet,
	txHeight *version.Height,
	db privacyenabledstate.DB,
	containsPostOrderWrites bool,
) error {
	u.PubUpdates.ContainsPostOrderWrites =
		u.PubUpdates.ContainsPostOrderWrites || containsPostOrderWrites
	txops, err := prepareTxOps(txRWSet, txHeight, u, db)
	logger.Debugf("txops=%#v", txops)
	if err != nil {
		return err
	}
	for compositeKey, keyops := range txops {
		if compositeKey.coll == "" {
			ns, key := compositeKey.ns, compositeKey.key
			if keyops.isDelete() {
				u.PubUpdates.Delete(ns, key, txHeight)
			} else {
				u.PubUpdates.PutValAndMetadata(ns, key, keyops.value, keyops.metadata, txHeight)
			}
		} else {
			ns, coll, keyHash := compositeKey.ns, compositeKey.coll, []byte(compositeKey.key)
			if keyops.isDelete() {
				u.HashUpdates.Delete(ns, coll, keyHash, txHeight)
			} else {
				u.HashUpdates.PutValHashAndMetadata(ns, coll, keyHash, keyops.value, keyops.metadata, txHeight)
			}
		}
	}
	return nil
}


text:
namespace CvSystem.Web.ViewModels.Home
{
    using System;

    using CvSystem.Data.Models;
    using CvSystem.Web.Infrastructure.Mapping;

    public class CertificationViewModel : IMapFrom<Certification>
    {
        public string CertificateName { get; set; }

        public string Authority { get; set; }

        public string LicenseNumber { get; set; }

        public string CertificateUrl { get; set; }

        public DateTime From { get; set; }

        public DateTime? To { get; set; }
    }
}


text:
using namespace System;

namespace Core {

	public ref class WTree1 : public Node
	{
	public:
		WTree1() { };//pInstance = &*pooptube::Ground::Create("E:/Project/Poop tube/2014-01-HUDIGAME-PoopTube/Pooptube/Client/test.bmp"); };
		virtual ~WTree1() { delete Instance<Tree1 *>(pInstance); };

		static WTree1 ^Create()
		{
			WTree1 ^CreateInstance = gcnew WTree1();
			CreateInstance->pInstance = Tree1::Create();
			CreateInstance->pInstance->SetUpdatable(false);
			return CreateInstance;
		}
	};
}


text:
from collections import deque

class PendingVMsPool:
    HIGH_PRIORITY = 0
    LOW_PRIORITY = 1

    def __init__(self):
        self._clear()

    def __len__(self):
        return len(self._high_priority) + len(self._low_priority)

    def _clear(self):
        self._logger = None
        self._config = None
        self._high_priority = deque()
        self._low_priority = deque()

    def set_config(self, config):
        self._config = config

    def initialize(self):
        self._logger = self._config.getLogger(self)

    def add_vm(self, vm, priority=LOW_PRIORITY):
        if priority == PendingVMsPool.HIGH_PRIORITY:
            self._high_priority.append(vm)
        else:
            self._low_priority.append(vm)

    def pop_next(self):
        if len(self._high_priority) > 0:
            return self._high_priority.popleft()
        elif len(self._low_priority) > 0:
            return self._low_priority.popleft()
        return None

    def get_ordered_list(self):
        return list(self._high_priority) + list(self._low_priority)

    def get_high_priority_vms(self):
        return list(self._high_priority)

    def get_low_priority_vms(self):
        return list(self._low_priority)

    def remove(self, vm):
        if vm in self._high_priority:
            self._high_priority.remove(vm)
        else:
            self._low_priority.remove(vm)


text:
package org.ehcache.clustered.client;

import org.ehcache.Cache;
import org.ehcache.PersistentCacheManager;
import org.ehcache.clustered.client.config.ClusteredStoreConfiguration;
import org.ehcache.clustered.client.internal.UnitTestConnectionService;
import org.ehcache.clustered.client.internal.UnitTestConnectionService.PassthroughServerBuilder;
import org.ehcache.clustered.common.Consistency;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.EntryUnit;
import org.ehcache.config.units.MemoryUnit;
import org.ehcache.core.internal.statistics.DefaultStatisticsService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.Serializable;
import java.math.BigInteger;
import java.net.URI;
import java.util.Random;

import static org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder.clusteredDedicated;
import static org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder.cluster;
import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder;
import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder;
import static org.ehcache.config.builders.ResourcePoolsBuilder.heap;
import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;

/**
 * Provides basic tests for creation of a cache using a {@link org.ehcache.clustered.client.internal.store.ClusteredStore ClusteredStore}.
 */
public class BasicClusteredCacheTest {

  private static final URI CLUSTER_URI = URI.create("terracotta://example.com:9540/my-application");

  @Before
  public void definePassthroughServer() throws Exception {
    UnitTestConnectionService.add(CLUSTER_URI,
        new PassthroughServerBuilder()
            .resource("primary-server-resource", 64, MemoryUnit.MB)
            .resource("secondary-server-resource", 64, MemoryUnit.MB)
            .build());
  }

  @After
  public void removePassthroughServer() throws Exception {
    UnitTestConnectionService.remove(CLUSTER_URI);
  }

  @Test
  public void testClusteredCacheSingleClient() throws Exception {

    final CacheManagerBuilder<PersistentCacheManager> clusteredCacheManagerBuilder =
        newCacheManagerBuilder()
            .with(cluster(CLUSTER_URI).autoCreate(c -> c))
            .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, String.class,
                ResourcePoolsBuilder.newResourcePoolsBuilder()
                    .with(clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))));
    try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) {

      final Cache<Long, String> cache = cacheManager.getCache("clustered-cache", Long.class, String.class);

      cache.put(1L, "value");
      assertThat(cache.get(1L), is("value"));
    }
  }

  @Test
  public void testClusteredCacheTwoClients() throws Exception {
    final CacheManagerBuilder<PersistentCacheManager> clusteredCacheManagerBuilder =
        newCacheManagerBuilder()
            .with(cluster(CLUSTER_URI).autoCreate(c -> c))
            .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, String.class,
                ResourcePoolsBuilder.newResourcePoolsBuilder().heap(100, EntryUnit.ENTRIES)
                    .with(clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB)))
                .withService(new ClusteredStoreConfiguration(Consistency.STRONG)));

    try (PersistentCacheManager cacheManager1 = clusteredCacheManagerBuilder.build(true)) {
      try (PersistentCacheManager cacheManager2 = clusteredCacheManagerBuilder.build(true)) {

        final Cache<Long, String> cache1 = cacheManager1.getCache("clustered-cache", Long.class, String.class);
        final Cache<Long, String> cache2 = cacheManager2.getCache("clustered-cache", Long.class, String.class);

        assertThat(cache2.get(1L), nullValue());
        cache1.put(1L, "value1");
        assertThat(cache2.get(1L), is("value1"));
        assertThat(cache1.get(1L), is("value1"));
        cache1.put(1L, "value2");
        assertThat(cache2.get(1L), is("value2"));
        assertThat(cache1.get(1L), is("value2"));
      }
    }
  }

  @Test
  public void testClustered3TierCacheTwoClients() throws Exception {
    final CacheManagerBuilder<PersistentCacheManager> clusteredCacheManagerBuilder =
        newCacheManagerBuilder()
            .with(cluster(CLUSTER_URI).autoCreate(c -> c))
            .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, String.class,
                ResourcePoolsBuilder.newResourcePoolsBuilder().heap(1, EntryUnit.ENTRIES).offheap(1, MemoryUnit.MB)
                    .with(clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB)))
                .withService(new ClusteredStoreConfiguration(Consistency.STRONG)));

    try (PersistentCacheManager cacheManager1 = clusteredCacheManagerBuilder.build(true)) {
      try (PersistentCacheManager cacheManager2 = clusteredCacheManagerBuilder.build(true)) {

        final Cache<Long, String> cache1 = cacheManager1.getCache("clustered-cache", Long.class, String.class);
        final Cache<Long, String> cache2 = cacheManager2.getCache("clustered-cache", Long.class, String.class);

        assertThat(cache2.get(1L), nullValue());
        cache1.put(1L, "value1");
        cache1.put(2L, "value2");
        cache1.put(3L, "value3");
        assertThat(cache2.get(1L), is("value1"));
        assertThat(cache2.get(2L), is("value2"));
        assertThat(cache2.get(3L), is("value3"));
        assertThat(cache2.get(1L), is("value1"));
        assertThat(cache2.get(2L), is("value2"));
        assertThat(cache2.get(3L), is("value3"));
        assertThat(cache1.get(1L), is("value1"));
        assertThat(cache1.get(2L), is("value2"));
        assertThat(cache1.get(3L), is("value3"));
        assertThat(cache1.get(1L), is("value1"));
        assertThat(cache1.get(2L), is("value2"));
        assertThat(cache1.get(3L), is("value3"));
        cache1.put(1L, "value11");
        cache1.put(2L, "value12");
        cache1.put(3L, "value13");
        assertThat(cache2.get(1L), is("value11"));
        assertThat(cache2.get(2L), is("value12"));
        assertThat(cache2.get(3L), is("value13"));
        assertThat(cache2.get(1L), is("value11"));
        assertThat(cache2.get(2L), is("value12"));
        assertThat(cache2.get(3L), is("value13"));
        assertThat(cache1.get(1L), is("value11"));
        assertThat(cache1.get(2L), is("value12"));
        assertThat(cache1.get(3L), is("value13"));
        assertThat(cache1.get(1L), is("value11"));
        assertThat(cache1.get(2L), is("value12"));
        assertThat(cache1.get(3L), is("value13"));
      }
    }
  }

  @Test
  public void testTieredClusteredCache() throws Exception {
    final CacheManagerBuilder<PersistentCacheManager> clusteredCacheManagerBuilder =
        newCacheManagerBuilder()
            .with(cluster(CLUSTER_URI).autoCreate(c -> c))
            .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, String.class,
                    heap(2)
                    .with(clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))));
    try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) {

      final Cache<Long, String> cache = cacheManager.getCache("clustered-cache", Long.class, String.class);

      cache.put(1L, "value");
      assertThat(cache.get(1L), is("value"));
    }
  }

  @Test
  public void testClusteredCacheWithSerializableValue() throws Exception {
    final CacheManagerBuilder<PersistentCacheManager> clusteredCacheManagerBuilder =
        newCacheManagerBuilder().with(cluster(CLUSTER_URI).autoCreate(c -> c))
            .withCache("clustered-cache", newCacheConfigurationBuilder(Long.class, Person.class,
                    newResourcePoolsBuilder().with(clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))));
    try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) {
      Cache<Long, Person> cache = cacheManager.getCache("clustered-cache", Long.class, Person.class);

      cache.put(38L, new Person("Clustered Joe", 28));
    }

    try (PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) {
      Cache<Long, Person> cache = cacheManager.getCache("clustered-cache", Long.class, Person.class);

      assertThat(cache.get(38L).name, is("Clustered Joe"));
    }
  }

  @Test
  public void testLargeValues() throws Exception {
    DefaultStatisticsService statisticsService = new DefaultStatisticsService();
    CacheManagerBuilder<PersistentCacheManager> clusteredCacheManagerBuilder =
            newCacheManagerBuilder()
                    .using(statisticsService)
                    .with(cluster(CLUSTER_URI).autoCreate(c -> c))
                    .withCache("small-cache", newCacheConfigurationBuilder(Long.class, BigInteger.class,
                            ResourcePoolsBuilder.newResourcePoolsBuilder()
                                    .with(clusteredDedicated("secondary-server-resource", 4, MemoryUnit.MB))));

    // The idea here is to add big things in the cache, and cause eviction of them to see if something crashes

    try(PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true)) {

      Cache<Long, BigInteger> cache = cacheManager.getCache("small-cache", Long.class, BigInteger.class);

      Random random = new Random();
      for (long i = 0; i < 100; i++) {
        BigInteger value = new BigInteger(30 * 1024 * 128 * (1 + random.nextInt(10)), random);
        cache.put(i, value);
      }
    }
  }

  public static class Person implements Serializable {

    private static final long serialVersionUID = 1L;

    final String name;
    final int age;

    public Person(String name, int age) {
      this.name = name;
      this.age = age;
    }
  }
}


text:
package com.team2052.frckrawler.fragments.dialog;

import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;

import com.team2052.frckrawler.R;
import com.team2052.frckrawler.activities.DatabaseActivity;
import com.team2052.frckrawler.database.RxDBManager;
import com.team2052.frckrawler.db.Event;
import com.team2052.frckrawler.db.Game;
import com.team2052.frckrawler.listeners.RefreshListener;

import java.util.Date;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * @author Adam
 * @since 12/23/2014.
 */
public class AddEventDialogFragment extends DialogFragment implements DialogInterface.OnClickListener {
    @BindView(R.id.name)
    EditText name;
    private Game mGame;

    public static AddEventDialogFragment newInstance(Game game) {
        AddEventDialogFragment fragment = new AddEventDialogFragment();
        Bundle b = new Bundle();
        b.putLong(DatabaseActivity.PARENT_ID, game.getId());
        fragment.setArguments(b);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.mGame = RxDBManager.getInstance(getActivity()).getGamesTable().load(getArguments().getLong(DatabaseActivity.PARENT_ID));
    }

    @Override
    //Build the dialog
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_add_event, null);
        ButterKnife.bind(this, view);
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppAlertDialogStyle);
        builder.setTitle("Add Event");
        builder.setPositiveButton("Add", this);
        builder.setNegativeButton("Cancel", this);
        builder.setView(view);
        return builder.create();
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        if (getParentFragment() != null && getParentFragment() instanceof RefreshListener) {
            ((RefreshListener) getParentFragment()).refresh();
        }
        super.onDismiss(dialog);
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        if (which == DialogInterface.BUTTON_POSITIVE) {
            Event event = new Event(null);
            event.setName(name.getText().toString());
            event.setGame(mGame);
            event.setFmsid(null);
            event.setDate(new Date());
            RxDBManager.getInstance(getContext()).getEventsTable().insert(event);
        } else {
            dismiss();
        }
    }
}


text:
class Analytics
  class_attribute :backend
  self.backend = AnalyticsRuby

  def initialize(user, params = {})
    @user = user
  end

  def track_signed_in
    track(event: "Signed In")
  end

  def track_activated(repo)
    track(
      event: "Activated Public Repo",
      properties: {
        name: repo.full_github_name
      }
    )
  end

  def track_deactivated(repo)
    track(
      event: "Deactivated Public Repo",
      properties: {
        name: repo.full_github_name
      }
    )
  end

  def track_reviewed(repo)
    track(
      event: "Reviewed Repo",
      properties: {
        name: repo.full_github_name
      }
    )
  end

  def track_subscribed(repo)
    track(
      event: "Subscribed Private Repo",
      properties: {
        name: repo.full_github_name,
        revenue: repo.plan_price
      }
    )
  end

  def track_unsubscribed(repo)
    track(
      event: "Unsubscribed Private Repo",
      properties: {
        name: repo.full_github_name,
        revenue: -repo.plan_price
      }
    )
  end

  private

  def track(options)
    backend.track({
      active_repos_count: user.repos.active.count,
      user_id: user.id,
    }.merge(options))
  end

  attr_reader :user
end


text:
@interface AmazonSearch : MZASISearch
{
    NSDictionary* mapping;
    NSDictionary* ratingsMap;
	NSString *accessKeyId, *secretAccessKey, *associateTag;
}
@property (retain) NSString *accessKeyId, *secretAccessKey, *associateTag;

+ (id)searchWithProvider:(id)provider delegate:(id<MZSearchProviderDelegate>)delegate url:(NSURL *)url parameters:(NSDictionary *)params;
- (id)initWithProvider:(id)provider delegate:(id<MZSearchProviderDelegate>)delegate url:(NSURL *)url parameters:(NSDictionary *)params;

- (NSString *)utcTimestamp;
- (NSString *)hmacStringForString:(NSString *)signatureInput;
- (NSString *)signatureInputForQueryString:(NSString *)queryString withUrl:(NSURL *)url;
- (NSString *)testQueryStringForParameterDictionary:(NSDictionary *)parameters;

@end


text:
<?php

function ShowAlert( $Message ) {
    echo '<script type="text/javascript">alert("' . $Message . '")</script>';
}


text:
package com.leonty.etmweb.service;

import java.io.IOException;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServletRequest;

import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.ui.velocity.VelocityEngineUtils;

@Service("emailService")
public class EmailService {

    @Autowired
    private JavaMailSender mailSender;

    @Autowired
    private VelocityEngine velocityEngine;

	@Autowired
	private HttpServletRequest request;

	@Autowired
	private ApplicationContext context;

    public void sendSimpleMail(String recipientEmail, String template, Map<String, Object> replacements)
            throws MessagingException {

        Properties props;
		try {
			props = PropertiesLoaderUtils.loadAllProperties("configuration.properties");

	        // Prepare message using a Spring helper
	        MimeMessage mimeMessage = this.mailSender.createMimeMessage();
	        MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8");

	        message.setSubject(context.getMessage("mail." + template + "Subject", null, Locale.getDefault()));

	        message.setFrom(props.getProperty("mail.from"));
	        message.setTo(recipientEmail);

	        replacements.put("baseUrl", getbaseUrl());

	        String emailContent = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, template + ".vm", replacements);

	        message.setText(emailContent, true);

	        // Send email
	        this.mailSender.send(mimeMessage);

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }

    private String getbaseUrl() {
		return request.getRequestURL().toString().replace(request.getServletPath(), "");
    }
}


text:
	<title>Jocelyn Jee Esien's panel show appearances</title>

	<link rel="stylesheet" media="all" href="../style.css" type="text/css"/>

Jocelyn Jee Esien's panel show appearances

Jocelyn Jee Esien (born 1979-03-02[ref]) has appeared in 3 episodes between 2008-2016.  Jocelyn Jee Esien on IMDB. Jocelyn Jee Esien on Wikipedia.

2008

2009

2010

2011

2012

2013

2014

2015

2016

2016-10-13 / It's Not What You Know

2015-03-05 / Listomania

2008-01-24 / Loose Women


text:
package com.cauchymop.goblob.ui;

import com.cauchymop.goblob.R;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;

@RunWith(AndroidJUnit4.class)
public class MainActivityTest {

  @Rule
  public ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>(MainActivity.class, false, false);

  @Test
  public void selectingNewGameOption_displaysGameTypeRadioChoices() throws Exception {

    activityRule.launchActivity(null);

    onView(withId(R.id.toolbar_match_spinner)).perform(click());
    onView(withText(R.string.new_game_label)).perform(click());

    onView(withId(R.id.game_type_radio_group)).check(matches(isDisplayed()));
  }

}


text:
#include "EMInterface.h"
#include "JITInstanceContext.h"

#include <assert.h>

namespace Jitrino {

PC_Handle ProfilingInterface::getPCHandle(ProfileType type) const {
    switch (type) {
        case ProfileType_EntryBackedge:
            return ebPCHandle;
        case ProfileType_Edge:
            return edgePCHandle;
        case ProfileType_Value:
            return valuePCHandle;
        default:
            assert(0);
    }
    return NULL;
}

MethodProfile* ProfilingInterface::getMethodProfile(MemoryManager& mm, ProfileType type, MethodDesc& md, JITProfilingRole role) const {

    Method_Profile_Handle mpHandle = profileAccessInterface->get_method_profile(emHandle, getPCHandle(type), md.getMethodHandle());
    if (mpHandle==0) {
        return NULL;
    }
    MethodProfile* p = NULL;
    if (type == ProfileType_Edge) {
        p = new (mm) EdgeMethodProfile(mpHandle, md, profileAccessInterface);
    } else if (type == ProfileType_Value) {
        p = new (mm) ValueMethodProfile(mpHandle, md, profileAccessInterface);
    } else {
        U_32* eCounter = (U_32*)profileAccessInterface->eb_profiler_get_entry_counter_addr(mpHandle);
        U_32* bCounter = (U_32*)profileAccessInterface->eb_profiler_get_backedge_counter_addr(mpHandle);
        p = new (mm) EntryBackedgeMethodProfile(mpHandle, md, eCounter, bCounter);
    }
    return p;
}

Method_Profile_Handle ProfilingInterface::getMethodProfileHandle(ProfileType type, MethodDesc& md) const {
    return profileAccessInterface->get_method_profile(emHandle, getPCHandle(type), md.getMethodHandle());
}

EM_PCTYPE ProfilingInterface::getProfileType(PC_Handle pch) const {
    return profileAccessInterface->get_pc_type(emHandle, pch);
}

bool ProfilingInterface::hasMethodProfile(ProfileType type, MethodDesc& md, JITProfilingRole role) const {

    PC_Handle pcHandle = getPCHandle(type);

    if (pcHandle == NULL) {
        return false;
    }
    if (jitRole != role) {
        return false;
    }
    if (profileAccessInterface != NULL) {
        Method_Profile_Handle mpHandle = profileAccessInterface->get_method_profile(emHandle, pcHandle, md.getMethodHandle());
        return mpHandle!=0;
    }
    return false;
}

U_32 ProfilingInterface::getProfileMethodCount(MethodDesc& md, JITProfilingRole role) const {
    assert(jitRole == role);
    PC_Handle pcHandle = getPCHandle(ProfileType_Edge);
    ProfileType pcType = ProfileType_Edge;
    if (pcHandle == NULL) {
        pcHandle = getPCHandle(ProfileType_EntryBackedge);
        pcType = ProfileType_EntryBackedge;
    }
    assert (pcHandle != NULL);
    Method_Handle methodHandle = md.getMethodHandle();
    Method_Profile_Handle mph = profileAccessInterface->get_method_profile(emHandle, pcHandle, methodHandle);
    if (mph == NULL) {
        return 0;
    }
    U_32* counterAddr = NULL;
    if (pcType == ProfileType_Edge) {
        counterAddr = (U_32*)profileAccessInterface->edge_profiler_get_entry_counter_addr(mph);
    } else {
        counterAddr = (U_32*)profileAccessInterface->eb_profiler_get_entry_counter_addr(mph);
    }
    return *counterAddr;
}

bool ProfilingInterface::enableProfiling(PC_Handle pc, JITProfilingRole role) {
    EM_PCTYPE _pcType =  profileAccessInterface->get_pc_type(emHandle, pc);
    if (_pcType != EM_PCTYPE_EDGE && _pcType != EM_PCTYPE_ENTRY_BACKEDGE && _pcType != EM_PCTYPE_VALUE) {
        return false;
    }
    JITInstanceContext* jitMode = JITInstanceContext::getContextForJIT(jitHandle);
    if (jitMode->isJet()) {
        if (role == JITProfilingRole_GEN) {
            profilingEnabled = _pcType == EM_PCTYPE_ENTRY_BACKEDGE;
        } else {
            profilingEnabled = false;
        }
    } else { //OPT
        profilingEnabled = true;
    }
    if (profilingEnabled) {
        jitRole = role;
        switch(_pcType)
        {
        case EM_PCTYPE_EDGE:
            edgePCHandle = pc;
            break;
        case EM_PCTYPE_ENTRY_BACKEDGE:
            ebPCHandle = pc;
            break;
        case EM_PCTYPE_VALUE:
            valuePCHandle = pc;
            break;
        default:
            assert(0);
            return false;
        }
    }
    return profilingEnabled;
}

bool ProfilingInterface::isProfilingEnabled(ProfileType pcType, JITProfilingRole role) const {
    if(!profilingEnabled || (jitRole != role) || (getPCHandle(pcType) == NULL)){
        return false;
    }
    return true;
}

EntryBackedgeMethodProfile* ProfilingInterface::createEBMethodProfile(MemoryManager& mm, MethodDesc& md) {
    assert(isProfilingEnabled(ProfileType_EntryBackedge, JITProfilingRole_GEN));
    PC_Handle pcHandle = getPCHandle(ProfileType_EntryBackedge);
    Method_Profile_Handle mpHandle = profileAccessInterface->eb_profiler_create_profile(pcHandle, md.getMethodHandle());
    assert(mpHandle!=0);
    U_32* eCounter = (U_32*)profileAccessInterface->eb_profiler_get_entry_counter_addr(mpHandle);
    U_32* bCounter = (U_32*)profileAccessInterface->eb_profiler_get_backedge_counter_addr(mpHandle);

    EntryBackedgeMethodProfile* p = new (mm) EntryBackedgeMethodProfile(mpHandle, md, eCounter, bCounter);
    return p;
}

EdgeMethodProfile* ProfilingInterface::createEdgeMethodProfile( MemoryManager& mm,
                                                                  MethodDesc& md,
                                                                  U_32 numCounters,
                                                                  U_32* counterKeys,
                                                                  U_32 checkSum )
{
    assert(isProfilingEnabled(ProfileType_Edge, JITProfilingRole_GEN));
    PC_Handle pcHandle = getPCHandle(ProfileType_Edge);
    Method_Profile_Handle mpHandle =  profileAccessInterface->edge_profiler_create_profile(
        pcHandle, md.getMethodHandle(), numCounters, counterKeys, checkSum);
    assert( mpHandle != NULL );

    EdgeMethodProfile* p = new (mm) EdgeMethodProfile(mpHandle, md, profileAccessInterface);
    return p;
}

ValueMethodProfile* ProfilingInterface::createValueMethodProfile(MemoryManager& mm,
                                                                    MethodDesc& md,
                                                                    U_32 numKeys,
                                                                    U_32* Keys)
{
    assert(isProfilingEnabled(ProfileType_Value, JITProfilingRole_GEN));
    PC_Handle pcHandle = getPCHandle(ProfileType_Value);
    Method_Profile_Handle mpHandle =  profileAccessInterface->value_profiler_create_profile(
        pcHandle, md.getMethodHandle(), numKeys, Keys);
    assert(mpHandle != NULL);

    ValueMethodProfile* p = new (mm) ValueMethodProfile(mpHandle, md, profileAccessInterface);
    return p;
}

U_32 ProfilingInterface::getMethodEntryThreshold() const {
    PC_Handle pcHandle = getPCHandle(ProfileType_Edge);
    if (pcHandle != NULL) {
        return profileAccessInterface->edge_profiler_get_entry_threshold(pcHandle);
    } else if ((pcHandle = getPCHandle(ProfileType_EntryBackedge)) != NULL) {
        return profileAccessInterface->eb_profiler_get_entry_threshold(pcHandle);
    }
    assert(0);
    return 0;
}

U_32 ProfilingInterface::getBackedgeThreshold() const {
    PC_Handle pcHandle = getPCHandle(ProfileType_Edge);
    if (pcHandle != NULL) {
        return profileAccessInterface->edge_profiler_get_backedge_threshold(pcHandle);
    } else if ((pcHandle = getPCHandle(ProfileType_EntryBackedge)) != NULL) {
        return profileAccessInterface->eb_profiler_get_backedge_threshold(pcHandle);
    }
    assert(0);
    return 0;
}

bool ProfilingInterface::isEBProfilerInSyncMode() const {
    PC_Handle pcHandle = getPCHandle(ProfileType_EntryBackedge);
    assert(pcHandle!=NULL);
    return profileAccessInterface->eb_profiler_is_in_sync_mode(pcHandle)!=0;
}

ProfilingInterface::PC_Callback_Fn* ProfilingInterface::getEBProfilerSyncModeCallback() const {
    assert(profileAccessInterface->eb_profiler_sync_mode_callback!=NULL);
    return (PC_Callback_Fn*)profileAccessInterface->eb_profiler_sync_mode_callback;
}

U_32  EdgeMethodProfile::getNumCounters() const {
    return profileAccessInterface->edge_profiler_get_num_counters(getHandle());
}

U_32  EdgeMethodProfile::getCheckSum() const {
    return profileAccessInterface->edge_profiler_get_checksum(getHandle());
}

U_32* EdgeMethodProfile::getEntryCounter() const {
    return (U_32*)profileAccessInterface->edge_profiler_get_entry_counter_addr(getHandle());
}

U_32* EdgeMethodProfile::getCounter(U_32 key) const  {
    U_32* counter = (U_32*)profileAccessInterface->edge_profiler_get_counter_addr(getHandle(), key);
    return counter;
}

POINTER_SIZE_INT ValueMethodProfile::getTopValue(U_32 instructionKey) const {
    return profileAccessInterface->value_profiler_get_top_value(getHandle(), instructionKey);
}

void ValueMethodProfile::dumpValues(std::ostream& os) const {
    profileAccessInterface->value_profiler_dump_values(getHandle(), os);
}

} //namespace


text:
import contextlib
import io
import json
import optparse
import textwrap
import unittest
from unittest.mock import patch

from blinkpy.common import path_finder
from blinkpy.common.net.git_cl import TryJobStatus
from blinkpy.common.net.git_cl_mock import MockGitCL
from blinkpy.common.net.rpc import Build, RPCError
from blinkpy.common.system.log_testing import LoggingTestCase
from blinkpy.tool.mock_tool import MockBlinkTool
from blinkpy.tool.commands.update_metadata import UpdateMetadata, MetadataUpdater
from blinkpy.web_tests.builder_list import BuilderList

path_finder.bootstrap_wpt_imports()
from manifest.manifest import Manifest

@patch('concurrent.futures.ThreadPoolExecutor.map', new=map)
class BaseUpdateMetadataTest(LoggingTestCase):
    def setUp(self):
        super().setUp()
        self.maxDiff = None

        self.tool = MockBlinkTool()
        self.finder = path_finder.PathFinder(self.tool.filesystem)
        self.tool.filesystem.write_text_file(
            self.finder.path_from_web_tests('external', 'wpt',
                                            'MANIFEST.json'),
            json.dumps({
                'version': 8,
                'items': {
                    'reftest': {
                        'fail.html': [
                            'c3f2fb6f436da59d43aeda0a7e8a018084557033',
                            [None, [['fail-ref.html', '==']], {}],
                        ],
                    },
                    'testharness': {
                        'pass.html': [
                            'd933fd981d4a33ba82fb2b000234859bdda1494e',
                            [None, {}],
                        ],
                        'crash.html': [
                            'd933fd981d4a33ba82fb2b000234859bdda1494e',
                            [None, {}],
                        ],
                        'variant.html': [
                            'b8db5972284d1ac6bbda0da81621d9bca5d04ee7',
                            ['variant.html?foo=bar/abc', {}],
                            ['variant.html?foo=baz', {}],
                        ],
                    },
                },
            }))
        self.tool.filesystem.write_text_file(
            self.finder.path_from_web_tests('wpt_internal', 'MANIFEST.json'),
            json.dumps({
                'version': 8,
                'url_base': '/wpt_internal/',
                'items': {
                    'testharness': {
                        'dir': {
                            'multiglob.https.any.js': [
                                'd6498c3e388e0c637830fa080cca78b0ab0e5305',
                                ['dir/multiglob.https.any.window.html', {}],
                                ['dir/multiglob.https.any.worker.html', {}],
                            ],
                        },
                    },
                },
            }))

    def _manifest_load_and_update(self,
                                  tests_root,
                                  manifest_path,
                                  url_base,
                                  types=None,
                                  **_):
        with self.tool.filesystem.open_text_file_for_reading(
                manifest_path) as manifest_file:
            manifest = json.load(manifest_file)
        return Manifest.from_json(tests_root,
                                  manifest,
                                  types=types,
                                  callee_owns_obj=True)

    @contextlib.contextmanager
    def _patch_builtins(self):
        with contextlib.ExitStack() as stack:
            stack.enter_context(self.tool.filesystem.patch_builtins())
            stack.enter_context(self.tool.executive.patch_builtins())
            stack.enter_context(
                patch('manifest.manifest.load_and_update',
                      self._manifest_load_and_update))
            yield stack

class UpdateMetadataExecuteTest(BaseUpdateMetadataTest):
    """Verify the tool's frontend and build infrastructure interactions."""

    def setUp(self):
        super().setUp()
        self.tool.builders = BuilderList({
            'test-linux-rel': {
                'port_name': 'test-linux-trusty',
                'specifiers': ['Trusty', 'Release'],
                'is_try_builder': True,
            },
            'test-mac-rel': {
                'port_name': 'test-mac-mac12',
                'specifiers': ['Mac12', 'Release'],
                'is_try_builder': True,
            },
            'Test Linux Tests': {
                'port_name': 'test-linux-trusty',
                'specifiers': ['Trusty', 'Release'],
            },
        })
        self.builds = {
            Build('test-linux-rel', 1000, '1000'):
            TryJobStatus.from_bb_status('FAILURE'),
            Build('test-mac-rel', 2000, '2000'):
            TryJobStatus.from_bb_status('SUCCESS'),
        }
        self.git_cl = MockGitCL(self.tool, self.builds)
        self.command = UpdateMetadata(self.tool, self.git_cl)

        self.tool.web.append_prpc_response({
            'artifacts': [{
                'artifactId':
                'wpt_reports_chrome_01.json',
                'fetchUrl':
                'https://cr.dev/123/wptreport.json?token=abc',
            }],
        })
        url = 'https://cr.dev/123/wptreport.json?token=abc'
        self.tool.web.urls[url] = json.dumps({
            'run_info': {
                'os': 'mac',
                'version': '12',
                'processor': 'arm',
                'bits': 64,
                'product': 'chrome',
            },
            'results': [{
                'test':
                '/crash.html',
                'subtests': [{
                    'name': 'this assertion crashes',
                    'status': 'CRASH',
                    'message': None,
                    'expected': 'PASS',
                    'known_intermittent': [],
                }],
                'expected':
                'OK',
                'status':
                'CRASH',
            }],
        }).encode()

    def _unstaged_changes(self):
        wpt_glob = self.finder.path_from_web_tests('external', 'wpt', '*.ini')
        return patch.object(
            self.command.git,
            'unstaged_changes',
            side_effect=lambda: self.tool.filesystem.glob(wpt_glob))

    def test_execute_all(self):
        with self._patch_builtins() as stack:
            stack.enter_context(self._unstaged_changes())
            exit_code = self.command.main([])
        self.assertEqual(exit_code, 0)
        # Even tests that pass may require an update if a subtest was added or
        # removed.
        self.assertLog([
            'INFO: All builds finished.\n',
            'INFO: Processing wptrunner report (1/1)\n',
            "INFO: Updated 'crash.html' (1/5, modified)\n",
            "INFO: Updated 'dir/multiglob.https.any.js' (2/5)\n",
            "INFO: Updated 'fail.html' (3/5)\n",
            "INFO: Updated 'pass.html' (4/5)\n",
            "INFO: Updated 'variant.html' (5/5)\n",
            'INFO: Staged 1 metadata file.\n',
        ])
        self.assertEqual(self.command.git.added_paths, {
            self.finder.path_from_web_tests('external', 'wpt',
                                            'crash.html.ini')
        })

    def test_execute_explicit_include_patterns(self):
        self.tool.filesystem.write_text_file(
            'test-names.txt',
            textwrap.dedent("""\
                # Ignore this line
                wpt_internal/dir
                  # Ignore this line too
                """))
        with self._patch_builtins():
            exit_code = self.command.main([
                '--test-name-file=test-names.txt',
                'variant.html',
                'pass.html',
            ])
        self.assertEqual(exit_code, 0)
        self.assertLog([
            'INFO: All builds finished.\n',
            'INFO: Processing wptrunner report (1/1)\n',
            "INFO: Updated 'dir/multiglob.https.any.js' (1/3)\n",
            "INFO: Updated 'pass.html' (2/3)\n",
            "INFO: Updated 'variant.html' (3/3)\n",
            'INFO: Staged 0 metadata files.\n',
        ])

    def test_execute_with_no_issue_number_aborts(self):
        self.command.git_cl = MockGitCL(self.tool, issue_number='None')
        with self._patch_builtins():
            exit_code = self.command.main([])
        self.assertEqual(exit_code, 1)
        self.assertLog(['ERROR: No issue number for current branch.\n'])

    def test_execute_trigger_jobs(self):
        self.command.git_cl = MockGitCL(
            self.tool, {
                Build('test-linux-rel', 1000, '1000'):
                TryJobStatus.from_bb_status('STARTED'),
            })
        with self._patch_builtins():
            exit_code = self.command.main([])
        self.assertEqual(exit_code, 1)
        self.assertLog([
            'INFO: No finished builds.\n',
            'INFO: Scheduled or started builds:\n',
            'INFO:   BUILDER              NUMBER  STATUS    BUCKET\n',
            'INFO:   test-linux-rel       1000    STARTED   try   \n',
            'INFO:   test-mac-rel         --      TRIGGERED try   \n',
            'ERROR: Once all pending try jobs have finished, '
            'please re-run the tool to fetch new results.\n',
        ])

    def test_execute_with_rpc_error(self):
        error = RPCError('mock error', 'getBuild', {'id': '123'}, 400)
        with self._patch_builtins() as stack:
            stack.enter_context(
                patch.object(self.command.git_cl.bb_client,
                             'execute_batch',
                             side_effect=error))
            exit_code = self.command.main(['--build=Test Linux Tests'])
            self.assertEqual(exit_code, 1)
            self.assertLog([
                'ERROR: getBuild: mock error (code: 400)\n',
                'ERROR: Request payload: {\n'
                '  "id": "123"\n'
                '}\n',
            ])

    def test_execute_no_trigger_jobs(self):
        self.command.git_cl = MockGitCL(self.tool, {})
        with self._patch_builtins():
            exit_code = self.command.main(['--no-trigger-jobs'])
        self.assertEqual(exit_code, 1)
        self.assertLog([
            "ERROR: Aborted: no try jobs and '--no-trigger-jobs' or "
            "'--dry-run' passed.\n",
        ])
        with self._patch_builtins():
            exit_code = self.command.main(['--dry-run'])
        self.assertEqual(exit_code, 1)
        self.assertLog([
            "ERROR: Aborted: no try jobs and '--no-trigger-jobs' or "
            "'--dry-run' passed.\n",
        ])
        self.assertEqual(self.command.git_cl.calls, [])

    def test_execute_dry_run(self):
        files_before = dict(self.tool.filesystem.files)
        with self._patch_builtins():
            exit_code = self.command.main(['--dry-run'])
        self.assertEqual(exit_code, 0)
        self.assertLog([
            'INFO: All builds finished.\n',
            'INFO: Processing wptrunner report (1/1)\n',
            "INFO: Updated 'crash.html' (1/5, modified)\n",
            "INFO: Updated 'dir/multiglob.https.any.js' (2/5)\n",
            "INFO: Updated 'fail.html' (3/5)\n",
            "INFO: Updated 'pass.html' (4/5)\n",
            "INFO: Updated 'variant.html' (5/5)\n",
        ])
        self.assertEqual(self.tool.filesystem.files, files_before)
        self.assertEqual(self.tool.executive.calls, [['luci-auth', 'token']])
        self.assertEqual(self.tool.git().added_paths, set())

    def test_execute_only_changed_tests(self):
        with self._patch_builtins() as stack:
            stack.enter_context(
                patch.object(
                    self.command.git,
                    'changed_files',
                    return_value=[
                        'third_party/blink/web_tests/external/wpt/crash.html',
                    ]))
            stack.enter_context(self._unstaged_changes())
            exit_code = self.command.main(['--only-changed-tests'])
        self.assertEqual(exit_code, 0)
        self.assertLog([
            'INFO: All builds finished.\n',
            'INFO: Processing wptrunner report (1/1)\n',
            "INFO: Updated 'crash.html' (1/1, modified)\n",
            'INFO: Staged 1 metadata file.\n',
        ])

    def test_execute_only_changed_tests_none(self):
        with self._patch_builtins() as stack:
            stack.enter_context(
                patch.object(self.command.git,
                             'changed_files',
                             return_value=[]))
            exit_code = self.command.main(['--only-changed-tests'])
        self.assertEqual(exit_code, 1)
        self.assertLog([
            'ERROR: No metadata to update.\n',
        ])

    def test_execute_abort_with_uncommitted_change(self):
        uncommitted_changes = [
            'third_party/blink/web_tests/external/wpt/fail.html.ini',
            'third_party/blink/web_tests/external/wpt/pass.html.ini',
            'third_party/blink/web_tests/external/wpt/variant.html.ini',
        ]
        with self._patch_builtins() as stack:
            stack.enter_context(
                patch.object(self.command.git,
                             'uncommitted_changes',
                             return_value=uncommitted_changes))
            exit_code = self.command.main(['fail.html', 'pass.html'])
        self.assertEqual(exit_code, 1)
        self.assertLog([
            'ERROR: Aborting: there are uncommitted metadata files:\n',
            'ERROR:   external/wpt/fail.html.ini\n',
            'ERROR:   external/wpt/pass.html.ini\n',
            'ERROR: Please commit or reset these files to continue.\n',
        ])

    def test_gather_reports(self):
        local_report = {
            'run_info': {
                'os': 'mac',
            },
            'results': [],
        }
        self.tool.filesystem.write_text_file(
            'report1.json', json.dumps(local_report, indent=2))
        # Simulate a retry within a shard.
        self.tool.filesystem.write_text_file(
            'report2.json', '\n'.join([json.dumps(local_report)] * 2))

        reports = list(
            self.command.gather_reports(self.builds,
                                        ['report1.json', 'report2.json']))
        self.assertEqual(len(reports), 3)
        self.assertLog([
            'INFO: Processing wptrunner report (1/3)\n',
            'INFO: Processing wptrunner report (2/3)\n',
            'INFO: Processing wptrunner report (3/3)\n',
        ])

    def test_gather_reports_no_artifacts(self):
        self.tool.web.responses.clear()
        self.tool.web.append_prpc_response({'artifacts': []})
        self.assertEqual(list(self.command.gather_reports(self.builds, [])),
                         [])
        self.assertLog([
            'WARNING: All builds are missing report artifacts.\n',
            'WARNING: No reports to process.\n',
        ])

class UpdateMetadataASTSerializationTest(BaseUpdateMetadataTest):
    """Verify the metadata ASTs are manipulated and written correctly.

    The update algorithm is already tested in wptrunner, but is particularly
    complex. This auxiliary test suite ensures Blink-specific scenarios work
    as intended.
    """

    def update(self, *reports, **options):
        result_defaults = {
            'subtests': [],
            'message': None,
            'duration': 1000,
            'expected': 'OK',
            'known_intermittent': [],
        }
        with self._patch_builtins():
            updater = MetadataUpdater.from_path_finder(self.finder, **options)
            for report in reports:
                report['run_info'] = {
                    'os': 'mac',
                    'version': '12',
                    'processor': 'arm',
                    'bits': 64,
                    'flag_specific': None,
                    'product': 'chrome',
                    'debug': False,
                    **(report.get('run_info') or {}),
                }
                report['results'] = [{
                    **result_defaults,
                    **result
                } for result in report['results']]
                buf = io.StringIO(json.dumps(report))
                updater.collect_results([buf])
            for test_file in updater.test_files_to_update():
                updater.update(test_file)

    def write_contents(self, path_to_metadata, contents):
        path = self.finder.path_from_web_tests(path_to_metadata)
        self.tool.filesystem.write_text_file(path, textwrap.dedent(contents))

    def assert_contents(self, path_to_metadata, contents):
        path = self.finder.path_from_web_tests(path_to_metadata)
        self.assertEqual(self.tool.filesystem.read_text_file(path),
                         textwrap.dedent(contents))

    def exists(self, path_to_metadata):
        path = self.finder.path_from_web_tests(path_to_metadata)
        return self.tool.filesystem.exists(path)

    def test_create_new_expectations(self):
        """The updater creates metadata for new unexpected results."""
        self.assertFalse(self.exists('external/wpt/variant.html.ini'))
        self.update({
            'results': [{
                'test': '/fail.html',
                'status': 'FAIL',
            }, {
                'test': '/fail.html',
                'status': 'OK',
            }],
        })
        # 'OK' is prioritized as the primary expected status.
        self.assert_contents(
            'external/wpt/fail.html.ini', """\
            [fail.html]
              expected: [OK, FAIL]
            """)

    def test_remove_all_pass(self):
        """The updater removes metadata for a test that became all-pass."""
        self.write_contents(
            'external/wpt/variant.html.ini', """\
            [variant.html?foo=baz]
              [formerly failing subtest]
                expected: FAIL
            """)
        self.update({
            'results': [{
                'test':
                '/variant.html?foo=baz',
                'status':
                'OK',
                'subtests': [{
                    'name': 'formerly failing subtest',
                    'status': 'PASS',
                    'message': None,
                    'expected': 'FAIL',
                    'known_intermittent': [],
                }],
            }],
        })
        self.assertFalse(self.exists('external/wpt/variant.html.ini'))

    def test_retain_other_keys(self):
        """The updater retains non-`expected` keys, even for all-pass tests."""
        self.write_contents(
            'external/wpt/variant.html.ini', """\
            [variant.html?foo=baz]
              implementation-status: implementing
              [formerly failing subtest]
                expected: FAIL
            """)
        self.update({
            'results': [{
                'test':
                '/variant.html?foo=baz',
                'status':
                'OK',
                'subtests': [{
                    'name': 'formerly failing subtest',
                    'status': 'PASS',
                    'message': None,
                    'expected': 'FAIL',
                    'known_intermittent': [],
                }],
            }],
        })
        self.assert_contents(
            'external/wpt/variant.html.ini', """\
            [variant.html?foo=baz]
              implementation-status: implementing
            """)

    def test_remove_nonexistent_subtest(self):
        """A full update cleans up subtests that no longer exist."""
        self.write_contents(
            'external/wpt/variant.html.ini', """\
            [variant.html?foo=baz]
              expected: CRASH

              [subtest that was removed]
                expected: CRASH
                custom_key: should not prevent removal
            """)
        self.update(
            {
                'results': [{
                    'test': '/variant.html?foo=baz',
                    'status': 'CRASH',
                    'subtests': [],
                }],
            },
            overwrite_conditions='yes')
        self.assert_contents(
            'external/wpt/variant.html.ini', """\
            [variant.html?foo=baz]
              expected: CRASH
            """)

    def test_keep_unobserved_subtest(self):
        """A partial update should not remove an unobserved subtest."""
        self.write_contents(
            'external/wpt/variant.html.ini', """\
            [variant.html?foo=baz]
              [subtest that should not be removed]
                expected: CRASH
            """)
        self.update(
            {
                'results': [{
                    'test': '/variant.html?foo=baz',
                    'status': 'CRASH',
                    'subtests': [],
                }],
            },
            overwrite_conditions='no')
        self.write_contents(
            'external/wpt/variant.html.ini', """\
            [variant.html?foo=baz]
              [subtest that should not be removed]
                expected: CRASH
            """)

    def test_no_change_for_expected(self):
        """The updater does not modify metadata for an expected result."""
        self.write_contents(
            'external/wpt/fail.html.ini', """\
            [fail.html]
              expected: [FAIL, CRASH]
            """)
        self.update(
            {
                'results': [{
                    'test': '/fail.html',
                    'status': 'CRASH',
                    'expected': 'FAIL',
                    'known_intermittent': ['CRASH'],
                }],
            },
            disable_intermittent='flaky')
        self.assert_contents(
            'external/wpt/fail.html.ini', """\
            [fail.html]
              expected: [FAIL, CRASH]
            """)

    def test_remove_stale_expectation(self):
        """The updater removes stale expectations by default."""
        self.write_contents(
            'external/wpt/fail.html.ini', """\
            [fail.html]
              expected: [OK, FAIL]
            """)
        self.update({
            'results': [{
                'test': '/fail.html',
                'status': 'FAIL',
                'expected': 'OK',
                'known_intermittent': ['FAIL'],
            }, {
                'test': '/fail.html',
                'status': 'CRASH',
                'expected': 'OK',
                'known_intermittent': ['FAIL'],
            }],
        })
        self.assert_contents(
            'external/wpt/fail.html.ini', """\
            [fail.html]
              expected: [FAIL, CRASH]
            """)

    def test_keep_existing_expectations(self):
        """The updater keeps stale expectations if explicitly requested."""
        self.write_contents(
            'external/wpt/fail.html.ini', """\
            [fail.html]
              expected: [OK, FAIL]
            """)
        self.update(
            {
                'results': [{
                    'test': '/fail.html',
                    'status': 'CRASH',
                    'expected': 'OK',
                    'known_intermittent': ['FAIL'],
                }],
            },
            keep_statuses=True)
        # The disable only works for flaky results in a single run.
        self.assert_contents(
            'external/wpt/fail.html.ini', """\
            [fail.html]
              expected: [CRASH, OK, FAIL]
            """)

    def test_disable_intermittent(self):
        """The updater can disable flaky tests.

        Note that the status list is not updated.
        """
        self.write_contents(
            'external/wpt/fail.html.ini', """\
            [fail.html]
              expected: FAIL
            """)
        self.update(
            {
                'results': [{
                    'test': '/fail.html',
                    'status': 'OK',
                    'expected': 'FAIL',
                }, {
                    'test': '/fail.html',
                    'status': 'FAIL',
                    'expected': 'FAIL',
                }],
            },
            disable_intermittent='flaky')
        self.assert_contents(
            'external/wpt/fail.html.ini', """\
            [fail.html]
              disabled: flaky
              expected: FAIL
            """)

    def test_update_bug_url(self):
        """The updater updates the 'bug' field for affected test IDs."""
        self.write_contents(
            'external/wpt/variant.html.ini', """\
            [variant.html?foo=bar/abc]
              bug: crbug.com/123

            [variant.html?foo=baz]
              bug: crbug.com/456
            """)
        self.update(
            {
                'results': [{
                    'test': '/variant.html?foo=baz',
                    'status': 'FAIL',
                }],
            },
            bug=789)
        self.assert_contents(
            'external/wpt/variant.html.ini', """\
            [variant.html?foo=bar/abc]
              bug: crbug.com/123

            [variant.html?foo=baz]
              bug: crbug.com/789
              expected: FAIL
            """)

    def test_condition_split(self):
        """A new status on a platform creates a new condition branch."""
        self.write_contents(
            'external/wpt/fail.html.ini', """\
            [fail.html]
              expected:
                if os == 'mac': FAIL
            """)
        self.update(
            {
                'run_info': {
                    'os': 'mac',
                    'version': '12',
                },
                'results': [{
                    'test': '/fail.html',
                    'status': 'TIMEOUT',
                    'expected': 'FAIL',
                }],
            }, {
                'run_info': {
                    'os': 'mac',
                    'version': '11',
                },
                'results': [{
                    'test': '/fail.html',
                    'status': 'FAIL',
                    'expected': 'FAIL',
                }],
            }, {
                'run_info': {
                    'os': 'win',
                    'version': '11',
                },
                'results': [{
                    'test': '/fail.html',
                    'status': 'OK',
                    'expected': 'OK',
                }],
            },
            overwrite_conditions='yes')
        path = self.finder.path_from_web_tests('external', 'wpt',
                                               'fail.html.ini')
        lines = self.tool.filesystem.read_text_file(path).splitlines()
        expected = textwrap.dedent("""\
            [fail.html]
              expected:
                if (os == "mac") and (version == "12"): TIMEOUT
                if (os == "mac") and (version == "11"): FAIL
                OK
            """)
        # TODO(crbug.com/1299650): The branch order appears unstable, which we
        # should fix upstream to avoid create spurious diffs.
        self.assertEqual(sorted(lines, reverse=True), expected.splitlines())

    def test_condition_merge(self):
        """Results that become property-agnostic consolidate conditions."""
        self.write_contents(
            'external/wpt/fail.html.ini', """\
            [fail.html]
              expected:
                if os == 'mac' and version == '11': FAIL
                if os == 'mac' and version == '12': TIMEOUT
            """)
        self.update(
            {
                'run_info': {
                    'os': 'mac',
                    'version': '12',
                },
                'results': [{
                    'test': '/fail.html',
                    'status': 'FAIL',
                    'expected': 'TIMEOUT',
                }],
            }, {
                'run_info': {
                    'os': 'mac',
                    'version': '11',
                },
                'results': [{
                    'test': '/fail.html',
                    'status': 'FAIL',
                    'expected': 'FAIL',
                }],
            }, {
                'run_info': {
                    'os': 'win',
                    'version': '11',
                },
                'results': [{
                    'test': '/fail.html',
                    'status': 'OK',
                    'expected': 'OK',
                }],
            },
            overwrite_conditions='yes')
        path = self.finder.path_from_web_tests('external', 'wpt',
                                               'fail.html.ini')
        contents = self.tool.filesystem.read_text_file(path)
        self.assertIn(
            contents, {
                textwrap.dedent("""\
                [fail.html]
                  expected:
                    if os == "mac": FAIL
                    OK
                """),
                textwrap.dedent("""\
                [fail.html]
                  expected:
                    if os == "win": OK
                    FAIL
                """),
            })

class UpdateMetadataArgumentParsingTest(unittest.TestCase):
    def setUp(self):
        self.tool = MockBlinkTool()
        self.command = UpdateMetadata(self.tool)

    @contextlib.contextmanager
    def assert_parse_error(self, expected_pattern):
        buf = io.StringIO()
        with contextlib.redirect_stderr(buf):
            with self.assertRaises(SystemExit):
                yield
        self.assertRegex(buf.getvalue(), expected_pattern)

    def test_build_syntax(self):
        options, _args = self.command.parse_args([
            '--build=Linux Tests:100,linux-rel', '--build=mac-rel:200',
            '--build=Mac12 Tests'
        ])
        self.assertEqual(options.builds, [
            Build('Linux Tests', 100),
            Build('linux-rel'),
            Build('mac-rel', 200),
            Build('Mac12 Tests'),
        ])
        with self.assert_parse_error('invalid build number'):
            self.command.parse_args(['--build=linux-rel:'])
        with self.assert_parse_error('invalid build number'):
            self.command.parse_args(['--build=linux-rel:nan'])

    def test_bug_number_patterns(self):
        options, _args = self.command.parse_args(['-b', '123'])
        self.assertEqual(options.bug, 123)
        options, _args = self.command.parse_args(['-b', 'crbug/123'])
        self.assertEqual(options.bug, 123)
        options, _args = self.command.parse_args(['--bug=crbug.com/123'])
        self.assertEqual(options.bug, 123)
        with self.assert_parse_error('invalid bug number or URL'):
            self.command.parse_args(['--bug=crbug.com/123a'])
        with self.assert_parse_error('invalid bug number or URL'):
            self.command.parse_args(['-b', 'cbug.com/123'])

    def test_report_files(self):
        self.tool.filesystem.write_text_file('path/to/a.json', 'a')
        self.tool.filesystem.write_text_file('path/to/b.json', 'b')
        options, _args = self.command.parse_args(
            ['--report=path/to/a.json', '--report=path/to/b.json'])
        self.assertEqual(options.reports, ['path/to/a.json', 'path/to/b.json'])

    def test_report_dir(self):
        self.tool.filesystem.write_text_file('path/to/logs/a.json', 'a')
        self.tool.filesystem.write_text_file('path/to/logs/b.json', 'b')
        # Do not recursively traverse, since it can be slow.
        self.tool.filesystem.write_text_file('path/to/logs/skip/nested/c.json',
                                             'c')
        options, _args = self.command.parse_args(['--report=path/to/logs'])
        self.assertEqual(options.reports, [
            'path/to/logs/a.json',
            'path/to/logs/b.json',
        ])

    def test_report_does_not_exist(self):
        with self.assert_parse_error('is neither a regular file '
                                     'nor a directory'):
            self.command.parse_args(['--report=does/not/exist'])


text:
require 'net/http'
require 'net/https'
require 'uri'
require 'optparse'

module Net
  class HTTPS < HTTP
    def self.post_form(url, params, headers, options={})
      request = Post.new(url.path)
      request.form_data = params
      request.initialize_http_header(headers)
      request.basic_auth url.user, url.password if url.user
      http = new(url.host, url.port)
      http.use_ssl = (url.scheme == 'https')
      if options[:certificate_bundle]
        http.ca_file = options[:certificate_bundle]
      end
      if options[:no_check_certificate]
        http.verify_mode = OpenSSL::SSL::VERIFY_NONE
      end
      http.start {|h| h.request(request) }
    end
  end
end

class RedmineMailHandler
  VERSION = '0.2.3'

  attr_accessor :verbose, :issue_attributes, :allow_override, :unknown_user, :default_group, :no_permission_check,
    :url, :key, :no_check_certificate, :certificate_bundle, :no_account_notice, :no_notification, :project_from_subaddress

  def initialize
    self.issue_attributes = {}

    optparse = OptionParser.new do |opts|
      opts.banner = "Usage: rdm-mailhandler.rb [options] --url=<Redmine URL> --key=<API key>"
      opts.separator("")
      opts.separator("Reads an email from standard input and forwards it to a Redmine server through a HTTP request.")
      opts.separator("")
      opts.separator("Required arguments:")
      opts.on("-u", "--url URL",              "URL of the Redmine server") {|v| self.url = v}
      opts.on("-k", "--key KEY",              "Redmine API key") {|v| self.key = v}
      opts.separator("")
      opts.separator("General options:")
      opts.on("--key-file FILE",              "full path to a file that contains your Redmine",
                                              "API key (use this option instead of --key if",
                                              "you don't want the key to appear in the command",
                                              "line)") {|v| read_key_from_file(v)}
      opts.on("--no-check-certificate",       "do not check server certificate") {self.no_check_certificate = true}
      opts.on("--certificate-bundle FILE",    "certificate bundle to use") {|v| self.certificate_bundle = v}
      opts.on("-h", "--help",                 "show this help") {puts opts; exit 1}
      opts.on("-v", "--verbose",              "show extra information") {self.verbose = true}
      opts.on("-V", "--version",              "show version information and exit") {puts VERSION; exit}
      opts.separator("")
      opts.separator("User and permissions options:")
      opts.on("--unknown-user ACTION",        "how to handle emails from an unknown user",
                                              "ACTION can be one of the following values:",
                                              "* ignore: email is ignored (default)",
                                              "* accept: accept as anonymous user",
                                              "* create: create a user account") {|v| self.unknown_user = v}
      opts.on("--no-permission-check",        "disable permission checking when receiving",
                                              "the email") {self.no_permission_check = '1'}
      opts.on("--default-group GROUP",        "add created user to GROUP (none by default)",
                                              "GROUP can be a comma separated list of groups") { |v| self.default_group = v}
      opts.on("--no-account-notice",          "don't send account information to the newly",
                                              "created user") { |v| self.no_account_notice = '1'}
      opts.on("--no-notification",            "disable email notifications for the created",
                                              "user") { |v| self.no_notification = '1'}
      opts.separator("")
      opts.separator("Issue attributes control options:")
      opts.on(      "--project-from-subaddress ADDR", "select project from subadress of ADDR found",
                                              "in To, Cc, Bcc headers") {|v| self.project_from_subaddress = v}
      opts.on("-p", "--project PROJECT",      "identifier of the target project") {|v| self.issue_attributes['project'] = v}
      opts.on("-s", "--status STATUS",        "name of the target status") {|v| self.issue_attributes['status'] = v}
      opts.on("-t", "--tracker TRACKER",      "name of the target tracker") {|v| self.issue_attributes['tracker'] = v}
      opts.on(      "--category CATEGORY",    "name of the target category") {|v| self.issue_attributes['category'] = v}
      opts.on(      "--priority PRIORITY",    "name of the target priority") {|v| self.issue_attributes['priority'] = v}
      opts.on(      "--fixed-version VERSION","name of the target version") {|v| self.issue_attributes['fixed_version'] = v}
      opts.on(      "--private",              "create new issues as private") {|v| self.issue_attributes['is_private'] = '1'}
      opts.on("-o", "--allow-override ATTRS", "allow email content to set attributes values",
                                              "ATTRS is a comma separated list of attributes",
                                              "or 'all' to allow all attributes to be",
                                              "overridable (see below for details)") {|v| self.allow_override = v}

      opts.separator <<-END_DESC

Overrides:
  ATTRS is a comma separated list of attributes among:
  * project, tracker, status, priority, category, assigned_to, fixed_version,
    start_date, due_date, estimated_hours, done_ratio
  * custom fields names with underscores instead of spaces (case insensitive)
  Example: --allow_override=project,priority,my_custom_field

  If the --project option is not set, project is overridable by default for
  emails that create new issues.

  You can use --allow_override=all to allow all attributes to be overridable.

Examples:
  No project specified, emails MUST contain the 'Project' keyword, otherwise
  they will be dropped (not recommanded):

    rdm-mailhandler.rb --url http://redmine.domain.foo --key secret

  Fixed project and default tracker specified, but emails can override
  both tracker and priority attributes using keywords:

    rdm-mailhandler.rb --url https://domain.foo/redmine --key secret \\
      --project myproject \\
      --tracker bug \\
      --allow-override tracker,priority

  Project selected by subaddress of redmine@example.net. Sending the email
  to redmine+myproject@example.net will add the issue to myproject:

    rdm-mailhandler.rb --url http://redmine.domain.foo --key secret \\
      --project-from-subaddress redmine@example.net
END_DESC

      opts.summary_width = 27
    end
    optparse.parse!

    unless url && key
      puts "Some arguments are missing. Use `rdm-mailhandler.rb --help` for getting help."
      exit 1
    end
  end

  def submit(email)
    uri = url.gsub(%r{/*$}, '') + '/mail_handler'

    headers = { 'User-Agent' => "Redmine mail handler/#{VERSION}" }

    data = { 'key' => key, 'email' => email,
                           'allow_override' => allow_override,
                           'unknown_user' => unknown_user,
                           'default_group' => default_group,
                           'no_account_notice' => no_account_notice,
                           'no_notification' => no_notification,
                           'no_permission_check' => no_permission_check,
                           'project_from_subaddress' => project_from_subaddress}
    issue_attributes.each { |attr, value| data["issue[#{attr}]"] = value }

    debug "Posting to #{uri}..."
    begin
      response = Net::HTTPS.post_form(URI.parse(uri), data, headers, :no_check_certificate => no_check_certificate, :certificate_bundle => certificate_bundle)
    rescue SystemCallError, IOError => e # connection refused, etc.
      warn "An error occured while contacting your Redmine server: #{e.message}"
      return 75 # temporary failure
    end
    debug "Response received: #{response.code}"

    case response.code.to_i
      when 403
        warn "Request was denied by your Redmine server. " +
             "Make sure that 'WS for incoming emails' is enabled in application settings and that you provided the correct API key."
        return 77
      when 422
        warn "Request was denied by your Redmine server. " +
             "Possible reasons: email is sent from an invalid email address or is missing some information."
        return 77
      when 400..499
        warn "Request was denied by your Redmine server (#{response.code})."
        return 77
      when 500..599
        warn "Failed to contact your Redmine server (#{response.code})."
        return 75
      when 201
        debug "Proccessed successfully"
        return 0
      else
        return 1
    end
  end

  private

  def debug(msg)
    puts msg if verbose
  end

  def read_key_from_file(filename)
    begin
      self.key = File.read(filename).strip
    rescue Exception => e
      $stderr.puts "Unable to read the key from #{filename}:\n#{e.message}"
      exit 1
    end
  end
end

handler = RedmineMailHandler.new
exit(handler.submit(STDIN.read))


text:
require 'spec_helper'

module Checkbot
  describe Discount do
    describe ".new" do
      subject { Discount.new(options) }
      let(:options) { {} }

      it { is_expected.to be_a(Discount) }
      it { is_expected.to be_a(Pack) }

      specify {
        expect(subject.conditions).to eq([])
        expect(subject.rewards).to eq([])
      }
    end

    describe "#to_s" do
      subject { mixed_pack.to_s }
      let(:mixed_pack) { Discount.new(options) }
      it { is_expected.to be_a(String) }

      let(:product1) { Product.new('product 1', 15) }
      let(:product2) { Product.new('product 2', 9.99) }
      let(:packables) do
        [
          Packable.new(product1, {quantity: 2}),
          Packable.new(product2, {quantity: 1})
        ]
      end
      let(:options) { { packables: packables } }

      context "when it's basic" do
        let(:mixed_pack) { Discount.new(options) }
        it { is_expected.to eq('#2P(product 1) & #1P(product 2)') }
      end

      context "when it has a name" do
        let(:options) do
          { name: 'discount name', packables: packables }
        end
        let(:mixed_pack) { Discount.new(options) }
        it { is_expected.to eq('discount name #2P(product 1) & #1P(product 2)') }
      end

      context "when it has savings" do
        let(:options) do
          { packables: packables, amount_off: 20 }
        end
        let(:mixed_pack) { Discount.new(options) }
        it { is_expected.to eq('#2P(product 1) & #1P(product 2) -> D-$20') }
      end
    end
  end
end