text:
<?php

namespace Exposure\Tests\Model;

use Exposure\Model\CommentOnProjectOwner,
    Exposure\Model\Comment,
    Exposure\Model\User,
    Sociable\Utility\StringValidator;

class CommentOnProjectOwnerTest extends \PHPUnit_Framework_TestCase {

    protected $comment;
    const STATUS = Comment::STATUS_PUBLISHED;
    protected $dateTime;
    const CONTENT = 'content';
    const RATING = 4;
    protected $from;
    protected $projectOwner;

    public function setUp() {
        $this->comment = new CommentOnProjectOwner();

        $this->dateTime = new \DateTime();
        $this->from = new User();
        $this->projectOwner = new User();
        $this->projectOwner->setType(User::TYPE_PROJECT_OWNER);
    }

    public function test__construct() {
        $this->assertInstanceOf('Exposure\Model\CommentOnProjectOwner', $this->comment);
        $this->assertEquals(Comment::TYPE_COMMENT_ON_PROJECT_OWNER, $this->comment->getType());
    }

    public function testSetProjectOwner_invalid() {
        $this->setExpectedException('Exposure\Model\CommentOnProjectOwnerException',
            CommentOnProjectOwner::EXCEPTION_OBJECT_USER_NOT_A_PROJECT_OWNER);
        $this->comment->setProjectOwner(new User());
    }

    public function testGetProjectOwner_invalid() {
        try {
            $this->comment->setProjectOwner(new User());
        }
        catch (\Exception $e) {}
        $this->assertNull($this->comment->getProjectOwner());
    }

    public function testSetProjectOwner_incorrecttype() {
        $this->projectOwner->setType(User::TYPE_SPONSOR);
        $this->setExpectedException('Exception');
        $this->comment->setProjectOwner($this->projectOwner);
    }

    public function testGetProjectOwner_incorrecttype() {
        $this->projectOwner->setType(User::TYPE_SPONSOR);
        try {
            $this->comment->setProjectOwner($this->projectOwner);
        }
        catch (\Exception $e) {}
        $this->assertNull($this->comment->getProjectOwner());
    }

    public function testSetGetProjectOwner() {
        $this->assertEquals($this->projectOwner,
                $this->comment->setProjectOwner($this->projectOwner));
        $this->assertEquals($this->projectOwner,
                $this->comment->getProjectOwner());
    }

    public function testValidate_missingpstatus() {
        $this->assertEquals(self::CONTENT, $this->comment->setContent(self::CONTENT));
        $this->assertEquals($this->dateTime, $this->comment->setDateTime($this->dateTime));
        $this->assertEquals($this->from, $this->comment->setFrom($this->from));
        $this->assertEquals($this->projectOwner, $this->comment->setProjectOwner($this->projectOwner));
        $this->setExpectedException('Exposure\Model\CommentException',
                CommentOnProjectOwner::EXCEPTION_INVALID_STATUS);
        $this->comment->validate();
    }

    public function testValidate_missingcontent() {
        $this->assertEquals(self::STATUS, $this->comment->setStatus(self::STATUS));
        $this->assertEquals($this->dateTime, $this->comment->setDateTime($this->dateTime));
        $this->assertEquals($this->from, $this->comment->setFrom($this->from));
        $this->assertEquals($this->projectOwner, $this->comment->setProjectOwner($this->projectOwner));
        $this->setExpectedException('Sociable\Utility\StringException',
            StringValidator::EXCEPTION_NOT_A_STRING);
        $this->comment->validate();
    }

    public function testValidate_missingdatetime() {
        $this->assertEquals(self::STATUS, $this->comment->setStatus(self::STATUS));
        $this->assertEquals(self::CONTENT, $this->comment->setContent(self::CONTENT));
        $this->assertEquals($this->from, $this->comment->setFrom($this->from));
        $this->assertEquals($this->projectOwner, $this->comment->setProjectOwner($this->projectOwner));
        $this->setExpectedException('Exposure\Model\CommentException',
                CommentOnProjectOwner::EXCEPTION_INVALID_DATE_TIME);
        $this->comment->validate();
    }

    public function testValidate_missingfrom() {
        $this->assertEquals(self::STATUS, $this->comment->setStatus(self::STATUS));
        $this->assertEquals(self::CONTENT, $this->comment->setContent(self::CONTENT));
        $this->assertEquals($this->dateTime, $this->comment->setDateTime($this->dateTime));
        $this->assertEquals($this->projectOwner, $this->comment->setProjectOwner($this->projectOwner));
        $this->setExpectedException('Exposure\Model\CommentException',
                CommentOnProjectOwner::EXCEPTION_INVALID_FROM);
        $this->comment->validate();
    }

    public function testValidate_missingprojectowner() {
        $this->assertEquals(self::STATUS, $this->comment->setStatus(self::STATUS));
        $this->assertEquals(self::CONTENT, $this->comment->setContent(self::CONTENT));
        $this->assertEquals($this->dateTime, $this->comment->setDateTime($this->dateTime));
        $this->assertEquals($this->from, $this->comment->setFrom($this->from));
        $this->setExpectedException('Exposure\Model\CommentOnProjectOwnerException',
                CommentOnProjectOwner::EXCEPTION_INVALID_PROJECT_OWNER);
        $this->comment->validate();
    }

    public function testValidate() {
        $this->assertEquals(self::STATUS, $this->comment->setStatus(self::STATUS));
        $this->assertEquals(self::CONTENT, $this->comment->setContent(self::CONTENT));
        $this->assertEquals($this->dateTime, $this->comment->setDateTime($this->dateTime));
        $this->assertEquals($this->from, $this->comment->setFrom($this->from));
        $this->assertEquals($this->projectOwner, $this->comment->setProjectOwner($this->projectOwner));
        $this->comment->validate();
        $this->assertEquals(self::RATING, $this->comment->setRating(self::RATING));
        $this->comment->validate();
    }

    public function tearDown() {
        unset($this->comment);
    }

}

?>


text:
package org.hawkular.metrics.core.service.metrics;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;

import static org.hawkular.metrics.model.AvailabilityType.DOWN;
import static org.hawkular.metrics.model.AvailabilityType.UP;
import static org.hawkular.metrics.model.MetricType.AVAILABILITY;
import static org.hawkular.metrics.model.MetricType.COUNTER;
import static org.hawkular.metrics.model.MetricType.COUNTER_RATE;
import static org.hawkular.metrics.model.MetricType.GAUGE;
import static org.joda.time.DateTime.now;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.hawkular.metrics.core.service.Order;
import org.hawkular.metrics.core.service.transformers.NumericDataPointCollector;
import org.hawkular.metrics.core.service.transformers.TaggedDataPointCollector;
import org.hawkular.metrics.model.AvailabilityType;
import org.hawkular.metrics.model.Buckets;
import org.hawkular.metrics.model.DataPoint;
import org.hawkular.metrics.model.Metric;
import org.hawkular.metrics.model.MetricId;
import org.hawkular.metrics.model.NumericBucketPoint;
import org.hawkular.metrics.model.Percentile;
import org.hawkular.metrics.model.TaggedBucketPoint;
import org.hawkular.metrics.model.Tenant;
import org.hawkular.metrics.model.param.BucketConfig;
import org.hawkular.metrics.model.param.TimeRange;
import org.joda.time.DateTime;
import org.testng.annotations.Test;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;

import rx.Observable;
import rx.observers.TestSubscriber;

public class TagsITest extends BaseMetricsITest {

    @SuppressWarnings({ "rawtypes" })
    @Test
    public void createAndFindMetricsWithTags() throws Exception {

        String tenantId = "t1";

        List<Metric<?>> metricsToAdd = createTagMetrics(tenantId);

        // Check different scenarios..
        List<MetricId<Double>> gauges = metricsService
                .findMetricIdentifiersWithFilters(tenantId, GAUGE, "a1:*")
                .toList()
                .toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 5, "Metrics m1-m5 should have been returned");

        gauges = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "a1:*,a2:2")
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 1, "Only metric m3 should have been returned");

        gauges = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "a1:*,a2:2|3")
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 2, "Metrics m3-m4 should have been returned");

        gauges = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "a2:2|3")
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 2, "Metrics m3-m4 should have been returned");

        gauges = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "a1:*,a2:*")
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 3, "Metrics m3-m5 should have been returned");

        gauges = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "a1:*,a5:*")
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 0, "No gauges should have been returned");

        gauges = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "a4:*,a5:none")
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 0, "No gauges should have been returned");

        List<MetricId<Object>> metrics = metricsService.findMetricIdentifiersWithFilters(tenantId, null, "a1:*")
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(metrics.size(), 6, "Metrics m1-m5 and a1 should have been returned");

        // Test that we actually get correct gauges also, not just correct size
        gauges = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "a1:2,a2:2")
                .toList().toBlocking().lastOrDefault(null);
        MetricId m3 = metricsToAdd.get(2).getMetricId();
        assertEquals(gauges.size(), 1, "Only metric m3 should have been returned");
        assertEquals(gauges.get(0), m3, "m3 did not match the original inserted metric");

        // Test for NOT operator
        gauges = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "a2:!4")
                .toList().toBlocking().lastOrDefault(null);
        System.out.println(gauges);
        assertEquals(gauges.size(), 2, "Only gauges m3-m4 should have been returned");

        gauges = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "a1:2,a2:!4")
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 2, "Only gauges m3-m4 should have been returned");

        gauges = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "a2:!4|3")
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 1, "Only gauges m3 should have been returned");
        assertEquals(gauges.get(0), m3, "m3 did not match the original inserted metric");

        // What about incorrect query?
        try {
            metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "a2:**")
                    .toList().toBlocking().lastOrDefault(null);
            fail("Should have thrown an PatternSyntaxException");
        } catch (PatternSyntaxException ignored) {
        }

        // More regexp tests
        gauges = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "hostname:web.*")
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 2, "Only websrv01 and websrv02 should have been returned");

        gauges = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "hostname:.*01")
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 2, "Only websrv01 and backend01 should have been returned");

        gauges = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "owner:h[e|a]de(s?)")
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 2, "Both hede and hades should have been returned, but not 'had'");

        gauges = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, "owner:h[e|a]de(s?)")
                .filter(metricsService.idFilter(".F"))
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 1, "Only hades should have been returned");

        // Not existing tags
        gauges = metricsService.<Double>findMetricIdentifiersWithFilters(tenantId, GAUGE, "!a2:*,a1:*")
                .doOnError(Throwable::printStackTrace)
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 2, "Only metrics with a1, but without a2 and type GAUGE should have been found");

        gauges = metricsService.<Double>findMetricIdentifiersWithFilters(tenantId, GAUGE, "!a1:*")
                .doOnError(Throwable::printStackTrace)
                .toList().toBlocking().lastOrDefault(null);
        assertEquals(gauges.size(), 8, "Only metrics without a1 and type GAUGE should have been found");
    }

    @Test
    public void findMetricsWithPodIdTagHavingMultipleValues() throws Exception {
        String tenantId = "PodIdQueryTestForMultipleIds";
        createMetricsWithPodIdTag(tenantId);

        String tags =
                "pod_id:871497e0-16b6-11e8-985b-54e1ad486be8|941497e0-16b6-11e8-985b-54e1ad486ae2," +
                "descriptor_name:memory/usage|cpu/usage," +
                "type:pod_container," +
                "container_name:myservice";
        List<MetricId<Double>> actual = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, tags)
                .toList()
                .toBlocking()
                .firstOrDefault(emptyList());
        List<MetricId<Double>> expected = ImmutableList.of(
                new MetricId<>(tenantId, GAUGE, "M1"),
                new MetricId<>(tenantId, GAUGE, "M2"),
                new MetricId<>(tenantId, GAUGE, "M3"),
                new MetricId<>(tenantId, GAUGE, "M4")
        );

        // convert to sets since we do not compare about order
        assertEquals(new HashSet<>(actual), new HashSet<>(expected),
                "The pod_id query returned unexpected results. Expected " + expected + " but got " + actual);
    }

    @Test
    public void findMetricsWithPodIdTagHavingSingleValue() throws Exception {
        String tenantId = "PodIdQueryTestForSingleId";
        createMetricsWithPodIdTag(tenantId);

        String tags =
                "pod_id:871497e0-16b6-11e8-985b-54e1ad486be8," +
                "descriptor_name:memory/usage," +
                "type:pod_container," +
                "container_name:myservice";

        List<MetricId<Double>> actual = metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, tags)
                .toList()
                .toBlocking()
                .firstOrDefault(emptyList());
        List<MetricId<Double>> expected = ImmutableList.of(new MetricId<>(tenantId, GAUGE, "M1"));

        // convert to sets since we do not compare about order
        assertEquals(new HashSet<>(actual), new HashSet<>(expected),
                "The pod_id query returned unexpected results. Expected " + expected + " but got " + actual);
    }

    private void createMetricsWithPodIdTag(String tenantId) {
        List<Metric<Double>> metrics = asList(
                new Metric<>(new MetricId<>(tenantId, GAUGE, "M1"), ImmutableMap.of(
                        "descriptor_name", "memory/usage",
                        "type", "pod_container",
                        "pod_id", "871497e0-16b6-11e8-985b-54e1ad486be8",
                        "container_name", "myservice"
                )),
                new Metric<>(new MetricId<>(tenantId, GAUGE, "M2"), ImmutableMap.of(
                        "descriptor_name", "cpu/usage",
                        "type", "pod_container",
                        "pod_id", "871497e0-16b6-11e8-985b-54e1ad486be8",
                        "container_name", "myservice"
                )),
                new Metric<>(new MetricId<>(tenantId, GAUGE, "M3"), ImmutableMap.of(
                        "descriptor_name", "memory/usage",
                        "type", "pod_container",
                        "pod_id", "941497e0-16b6-11e8-985b-54e1ad486ae2",
                        "container_name", "myservice"
                )),
                new Metric<>(new MetricId<>(tenantId, GAUGE, "M4"), ImmutableMap.of(
                        "descriptor_name", "cpu/usage",
                        "type", "pod_container",
                        "pod_id", "941497e0-16b6-11e8-985b-54e1ad486ae2",
                        "container_name", "myservice"
                )),
                new Metric<>(new MetricId<>(tenantId, GAUGE, "M5"), ImmutableMap.of(
                        "descriptor_name", "memory/usage",
                        "type", "pod_container",
                        "pod_id", "541497e0-16b6-33e8-981c-54e1ad486ae2",
                        "container_name", "nodeapp"
                )),
                new Metric<>(new MetricId<>(tenantId, GAUGE, "M6"), ImmutableMap.of(
                        "descriptor_name", "cpu/usage",
                        "type", "pod_container",
                        "pod_id", "541497e0-16b6-33e8-981c-54e1ad486ae2",
                        "container_name", "nodeapp"
                )),
                new Metric<>(new MetricId<>(tenantId, GAUGE, "M7"), ImmutableMap.of(
                        "descriptor_name", "memory/page_faults",
                        "type", "pod_container",
                        "pod_id", "871497e0-16b6-11e8-985b-54e1ad486be8",
                        "container_name", "myservice"
                )),
                new Metric<>(new MetricId<>(tenantId, GAUGE, "M8"), ImmutableMap.of(
                        "descriptor_name", "memory/major_page_faults",
                        "type", "pod_container",
                        "pod_id", "871497e0-16b6-11e8-985b-54e1ad486be8",
                        "container_name", "myservice"
                )),
                new Metric<>(new MetricId<>(tenantId, GAUGE, "M7"), ImmutableMap.of(
                        "descriptor_name", "cpu/usage",
                        "type", "deployment",
                        "pod_id", "93a697e0-16b6-11e8-985b-54e1ad486be8",
                        "container_name", "myservice"
                )),
                new Metric<>(new MetricId<>(tenantId, GAUGE, "M8"), ImmutableMap.of(
                        "descriptor_name", "memory/usage",
                        "type", "deployment",
                        "pod_id", "93a697e0-16b6-11e8-985b-54e1ad486be8",
                        "container_name", "myservice"
                )),
                new Metric<>(new MetricId<>(tenantId, GAUGE, "M9"), ImmutableMap.of(
                        "descriptor_name", "cpu/usage_rate",
                        "type", "pod_container",
                        "pod_id", "871497e0-16b6-11e8-985b-54e1ad486be8",
                        "container_name", "myservice"
                )),new Metric<>(new MetricId<>(tenantId, GAUGE, "M10"), ImmutableMap.of(
                        "descriptor_name", "cpu/usage_rate",
                        "type", "pod_container",
                        "pod_id", "941497e0-16b6-11e8-985b-54e1ad486ae2",
                        "container_name", "myservice"
                )),
                new Metric<>(new MetricId<>(tenantId, GAUGE, "M10"), ImmutableMap.of(
                        "descriptor_name", "cpu/usage_rate",
                        "type", "pod_container",
                        "pod_id", "541497e0-16b6-33e8-981c-54e1ad486ae2",
                        "container_name", "nodeapp"
                ))
        );

        metrics.forEach(metric -> metricsService.createMetric(metric, true).toBlocking().lastOrDefault(null));
    }

    @Test
    public void tagValueSearch() throws Exception {
        String tenantId = "t1tag";

        createTagMetrics(tenantId);

        // Test only tags value fetching
        Map<String, Set<String>> tagMap = metricsService
                .getTagValues(tenantId, null, ImmutableMap.of("hostname", "*"))
                .toBlocking()
                .lastOrDefault(null);

        Set<String> hostnameSet = tagMap.get("hostname");
        assertEquals(hostnameSet.size(), 4, "There should have been 4 hostname tag values");

        tagMap = metricsService.getTagValues(tenantId, null, ImmutableMap.of("a1", "*", "a2", "*"))
                .toBlocking()
                .lastOrDefault(null);

        assertEquals(tagMap.size(), 2, "We should have two keys, a1 and a2");
        assertEquals(tagMap.get("a1").size(), 1, "a1 should have only one valid value");
        assertEquals(tagMap.get("a2").size(), 3, "a2 should have three values");

        tagMap = metricsService.getTagValues(tenantId, AVAILABILITY, ImmutableMap.of("a1", "*"))
                .toBlocking()
                .lastOrDefault(null);
        assertEquals(tagMap.get("a1").size(), 1, "a1 should have only one valid value");
    }

    @Test
    public void tagNameSearch() throws Exception {
        String tenantId = "t1tag";
        createTagMetrics(tenantId);

        Observable<String> tagNames = metricsService.getTagNames(tenantId, null, null);
        TestSubscriber<String> testSubscriber = new TestSubscriber<>();
        tagNames.subscribe(testSubscriber);

        testSubscriber.awaitTerminalEvent(5, TimeUnit.SECONDS);
        testSubscriber.assertCompleted();
        testSubscriber.assertValueCount(5);

        tagNames = metricsService.getTagNames(tenantId, null, "host.*");
        testSubscriber = new TestSubscriber<>();
        tagNames.subscribe(testSubscriber);

        testSubscriber.awaitTerminalEvent(5, TimeUnit.SECONDS);
        testSubscriber.assertCompleted();
        testSubscriber.assertValueCount(1);

        tagNames = metricsService.getTagNames(tenantId, AVAILABILITY, null);
        testSubscriber = new TestSubscriber<>();
        tagNames.subscribe(testSubscriber);

        testSubscriber.awaitTerminalEvent(5, TimeUnit.SECONDS);
        testSubscriber.assertCompleted();
        testSubscriber.assertValueCount(1);
    }

    @Test(expectedExceptions = IllegalArgumentException.class)
    public void doNotAllowInvalidMetricTags() {
        Map<String, String> invalidTagMap = ImmutableMap.of("key1", "value1", "", "value2");

        metricsService.addTags(new Metric<>(new MetricId<>("test", COUNTER_RATE, "counter-rate")), invalidTagMap)
                .toBlocking().lastOrDefault(null);
    }

    @Test(expectedExceptions = IllegalArgumentException.class)
    public void doNotAllowEmptyMetricTags() {
        metricsService.addTags(new Metric<>(new MetricId<>("test", COUNTER_RATE, "counter-rate")), null)
                .toBlocking()
                .lastOrDefault(null);
    }

    @Test
    public void updateMetricTags() throws Exception {
        Metric<Double> metric = new Metric<>(new MetricId<>("t1", GAUGE, "m1"),
                ImmutableMap.of("a1", "1", "a2", "2"), DEFAULT_TTL);
        metricsService.createMetric(metric, false).toBlocking().lastOrDefault(null);

        Map<String, String> additions = ImmutableMap.of("a2", "two", "a3", "3");
        metricsService.addTags(metric, additions).toBlocking().lastOrDefault(null);

        Set<String> deletions = ImmutableSet.of("a1");
        metricsService.deleteTags(metric, deletions).toBlocking()
                .lastOrDefault(null);

        Metric<?> updatedMetric = metricsService.findMetric(metric.getMetricId()).toBlocking()
                .last();

        assertEquals(updatedMetric.getTags(), ImmutableMap.of("a2", "two", "a3", "3"),
                "The updated meta data does not match the expected values");

        assertMetricIndexMatches(metric.getMetricId().getTenantId(), GAUGE, singletonList(updatedMetric));
    }

    @SuppressWarnings("unchecked")
    protected List<Metric<?>> createTagMetrics(String tenantId) throws Exception {
        ImmutableList<MetricId<?>> ids = ImmutableList.of(
                new MetricId<>(tenantId, GAUGE, "m1"),
                new MetricId<>(tenantId, GAUGE, "m2"),
                new MetricId<>(tenantId, GAUGE, "m3"),
                new MetricId<>(tenantId, GAUGE, "m4"),
                new MetricId<>(tenantId, GAUGE, "m5"),
                new MetricId<>(tenantId, GAUGE, "m6"),
                new MetricId<>(tenantId, GAUGE, "mA"),
                new MetricId<>(tenantId, GAUGE, "mB"),
                new MetricId<>(tenantId, GAUGE, "mC"),
                new MetricId<>(tenantId, GAUGE, "mD"),
                new MetricId<>(tenantId, GAUGE, "mE"),
                new MetricId<>(tenantId, GAUGE, "mF"),
                new MetricId<>(tenantId, GAUGE, "mG"),
                new MetricId<>(tenantId, AVAILABILITY, "a1"));

        ImmutableList<ImmutableMap<String, String>> maps = ImmutableList.of(
                ImmutableMap.of("a1", "1"),
                ImmutableMap.of("a1", "2", "a3", "3"),
                ImmutableMap.of("a1", "2", "a2", "2"),
                ImmutableMap.of("a1", "2", "a2", "3"),
                ImmutableMap.of("a1", "2", "a2", "4"),
                ImmutableMap.of("a2", "4"),
                ImmutableMap.of("hostname", "webfin01"),
                ImmutableMap.of("hostname", "webswe02"),
                ImmutableMap.of("hostname", "backendfin01"),
                ImmutableMap.of("hostname", "backendswe02"),
                ImmutableMap.of("owner", "hede"),
                ImmutableMap.of("owner", "hades"),
                ImmutableMap.of("owner", "had"),
                ImmutableMap.of("a1", "4"));
        assertEquals(ids.size(), maps.size(), "ids' size should equal to maps' size");

        // Create the metrics
        List<Metric<?>> metricsToAdd = new ArrayList<>(ids.size());
        for (int i = 0; i < ids.size(); i++) {
            if(ids.get(i).getType() == GAUGE) {
                metricsToAdd.add(new Metric<>((MetricId<Double>) ids.get(i), maps.get(i), 24, asList(
                        new DataPoint<>(System.currentTimeMillis(), 1.0))));
            } else {
                metricsToAdd.add(new Metric<>(ids.get(i), maps.get(i), 24));
            }
        }

        // Insert metrics
        Observable.from(metricsToAdd)
                .subscribe(m -> metricsService.createMetric(m, false).toBlocking().lastOrDefault(null));

        return metricsToAdd;
    }

    @Test
    public void addTaggedGaugeDataPoints() throws Exception {
        DateTime start = now().minusMinutes(30);
        DateTime end = start.plusMinutes(20);
        String tenantId = "tagged-data-points";

        Metric<Double> metric = new Metric<>(new MetricId<>(tenantId, GAUGE, "G1"), asList(
                new DataPoint<>(start.plusMinutes(4).getMillis(), 1.1, ImmutableMap.of("x", "1")),
                new DataPoint<>(start.plusMinutes(2).getMillis(), 2.2, ImmutableMap.of("x", "2", "y", "5")),
                new DataPoint<>(start.getMillis(), 3.4)));

        doAction(() -> metricsService.addDataPoints(GAUGE, Observable.just(metric)));

        Observable<DataPoint<Double>> data = metricsService.findDataPoints(new MetricId<>(tenantId, GAUGE, "G1"),
                start.getMillis(), end.getMillis(), 0, Order.ASC);
        List<DataPoint<Double>> actual = toList(data);
        List<DataPoint<Double>> expected = asList(
                new DataPoint<>(start.getMillis(), 3.4),
                new DataPoint<>(start.plusMinutes(2).getMillis(), 2.2, ImmutableMap.of("x", "2", "y", "5")),
                new DataPoint<>(start.plusMinutes(4).getMillis(), 1.1, ImmutableMap.of("x", "1")));

        assertEquals(actual, expected, "The tagged data does not match the expected values");
        assertMetricIndexMatches(tenantId, GAUGE, singletonList(new Metric<>(metric.getMetricId(),
                metric.getDataPoints(), 7)));

        data = metricsService.findDataPoints(new MetricId<>(tenantId, GAUGE, "G1"), start.getMillis(),
                end.getMillis(), 1, Order.ASC);
        actual = toList(data);
        expected = singletonList(new DataPoint<>(start.getMillis(), 3.4));

        assertEquals(actual, expected, "The tagged data does not match when limiting the number of data points");

        data = metricsService.findDataPoints(new MetricId<>(tenantId, GAUGE, "G1"), start.getMillis(),
                end.getMillis(), 0, Order.DESC);
        actual = toList(data);
        expected = asList(
                new DataPoint<>(start.plusMinutes(4).getMillis(), 1.1, ImmutableMap.of("x", "1")),
                new DataPoint<>(start.plusMinutes(2).getMillis(), 2.2, ImmutableMap.of("x", "2", "y", "5")),
                new DataPoint<>(start.getMillis(), 3.4));

        assertEquals(actual, expected, "The tagg data does not match when the order is DESC");

        data = metricsService.findDataPoints(new MetricId<>(tenantId, GAUGE, "G1"), start.getMillis(),
                end.getMillis(), 1, Order.DESC);
        actual = toList(data);
        expected = singletonList(new DataPoint<>(start.plusMinutes(4).getMillis(), 1.1, ImmutableMap.of("x", "1")));

        assertEquals(actual, expected, "The tagged data does not match when the order is DESC and there is a limit");
    }

    @Test
    public void addTaggedAvailabilityDataPoints() throws Exception {
        DateTime start = now().minusMinutes(30);
        DateTime end = start.plusMinutes(20);
        String tenantId = "tagged-availability";

        Metric<AvailabilityType> metric = new Metric<>(new MetricId<>(tenantId, AVAILABILITY, "A1"), asList(
                new DataPoint<>(start.plusMinutes(4).getMillis(), UP, ImmutableMap.of("x", "1")),
                new DataPoint<>(start.plusMinutes(2).getMillis(), DOWN, ImmutableMap.of("x", "2", "y", "3")),
                new DataPoint<>(start.getMillis(), UP)));

        doAction(() -> metricsService.addDataPoints(AVAILABILITY, Observable.just(metric)));

        Observable<DataPoint<AvailabilityType>> data = null;
        try {
//            Observable<DataPoint<AvailabilityType>>
                    data = metricsService.findDataPoints(new MetricId<>(tenantId,
                    AVAILABILITY, "A1"), start.getMillis(), end.getMillis(), 0, Order.ASC);
        } catch(NullPointerException e) {
            e.printStackTrace();
        }
        List<DataPoint<AvailabilityType>> actual = toList(data);
        List<DataPoint<AvailabilityType>> expected = asList(
                new DataPoint<>(start.getMillis(), UP),
                new DataPoint<>(start.plusMinutes(2).getMillis(), DOWN, ImmutableMap.of("x", "2", "y", "3")),
                new DataPoint<>(start.plusMinutes(4).getMillis(), UP, ImmutableMap.of("x", "1")));

        assertEquals(actual, expected, "The tagged availability data points do not match");
        assertMetricIndexMatches(tenantId, AVAILABILITY, singletonList(new Metric<>(metric.getMetricId(),
                metric.getDataPoints(), 7)));

        data = metricsService.findDataPoints(new MetricId<>(tenantId, AVAILABILITY, "A1"), start.getMillis(),
                end.getMillis(), 1, Order.ASC);
        actual = toList(data);
        expected = singletonList(new DataPoint<>(start.getMillis(), UP));

        assertEquals(actual, expected, "The tagged availability data points do not match when there is a limit");

        data = metricsService.findDataPoints(new MetricId<>(tenantId, AVAILABILITY, "A1"), start.getMillis(),
                end.getMillis(), 0, Order.DESC);
        actual = toList(data);
        expected = asList(
                new DataPoint<>(start.plusMinutes(4).getMillis(), UP, ImmutableMap.of("x", "1")),
                new DataPoint<>(start.plusMinutes(2).getMillis(), DOWN, ImmutableMap.of("x", "2", "y", "3")),
                new DataPoint<>(start.getMillis(), UP));

        assertEquals(actual, expected, "The tagged availability data points do not match when the order is DESC");

        data = metricsService.findDataPoints(new MetricId<>(tenantId, AVAILABILITY, "A1"), start.getMillis(),
                end.getMillis(), 1, Order.DESC);
        actual = toList(data);
        expected = singletonList(new DataPoint<>(start.plusMinutes(4).getMillis(), UP, ImmutableMap.of("x", "1")));

        assertEquals(actual, expected, "The tagged availability data points do not match when the order is DESC and " +
                "there is a limit");
    }

    @Test
    public void addTaggedCounterDataPoints() throws Exception {
        DateTime start = now().minusMinutes(30);
        DateTime end = start.plusMinutes(20);
        String tenantId = "counters-tenant";

        doAction(() -> metricsService.createTenant(new Tenant(tenantId), false));

        Metric<Long> counter = new Metric<>(new MetricId<>(tenantId, COUNTER, "c1"), asList(
                new DataPoint<>(start.getMillis(), 10L, ImmutableMap.of("x", "1")),
                new DataPoint<>(start.plusMinutes(2).getMillis(), 15L, ImmutableMap.of("x", "2", "y", "3")),
                new DataPoint<>(start.plusMinutes(4).getMillis(), 25L, ImmutableMap.of("z", "5")),
                new DataPoint<>(end.getMillis(), 45L)));

        doAction(() -> metricsService.addDataPoints(COUNTER, Observable.just(counter)));

        Observable<DataPoint<Long>> data = metricsService.findDataPoints(new MetricId<>(tenantId, COUNTER, "c1"),
                start.getMillis(), end.getMillis(), 0, Order.DESC);
        List<DataPoint<Long>> actual = toList(data);
        List<DataPoint<Long>> expected = asList(
                new DataPoint<>(start.plusMinutes(4).getMillis(), 25L, ImmutableMap.of("z", "5")),
                new DataPoint<>(start.plusMinutes(2).getMillis(), 15L, ImmutableMap.of("x", "2", "y", "3")),
                new DataPoint<>(start.getMillis(), 10L, ImmutableMap.of("x", "1")));

        assertEquals(actual, expected, "The tagged counter data does not match the expected values");
        assertMetricIndexMatches(tenantId, COUNTER,
                singletonList(new Metric<>(counter.getMetricId(), counter.getDataPoints(), 7)));

        data = metricsService.findDataPoints(new MetricId<>(tenantId, COUNTER, "c1"), start.getMillis(),
                end.getMillis(), 1, Order.DESC);
        actual = toList(data);
        expected = singletonList(new DataPoint<>(start.plusMinutes(4).getMillis(), 25L, ImmutableMap.of("z", "5")));

        assertEquals(actual, expected, "The tagged counter data does not match when there is a limit");

        data = metricsService.findDataPoints(new MetricId<>(tenantId, COUNTER, "c1"), start.getMillis(),
                end.getMillis(), 0, Order.ASC);
        actual = toList(data);
        expected = asList(
                new DataPoint<>(start.getMillis(), 10L, ImmutableMap.of("x", "1")),
                new DataPoint<>(start.plusMinutes(2).getMillis(), 15L, ImmutableMap.of("x", "2", "y", "3")),
                new DataPoint<>(start.plusMinutes(4).getMillis(), 25L, ImmutableMap.of("z", "5")));

        assertEquals(actual, expected, "The tagged counter data does not match when the order is ASC");

        data = metricsService.findDataPoints(new MetricId<>(tenantId, COUNTER, "c1"), start.getMillis(),
                end.getMillis(), 1, Order.ASC);
        actual = toList(data);
        expected = singletonList(new DataPoint<>(start.getMillis(), 10L, ImmutableMap.of("x", "1")));

        assertEquals(actual, expected, "The tagged counter data does not match when the order is ASC and there is a " +
                "limit");
    }

    @Test
    public void findCounterStats() {
        String tenantId = "counter-stats-test";
        long now = now().getMillis();

        Metric<Long> counter = new Metric<>(new MetricId<>(tenantId, COUNTER, "C1"), asList(
                new DataPoint<>((long) (now + 60_000 * 1.0), 0L),
                new DataPoint<>((long) (now + 60_000 * 1.5), 200L),
                new DataPoint<>((long) (now + 60_000 * 3.5), 400L),
                new DataPoint<>((long) (now + 60_000 * 5.0), 550L),
                new DataPoint<>((long) (now + 60_000 * 7.0), 950L),
                new DataPoint<>((long) (now + 60_000 * 7.5), 1000L)));

        doAction(() -> metricsService.addDataPoints(COUNTER, Observable.just(counter)));

        BucketConfig bucketConfig = mock(BucketConfig.class);
        when(bucketConfig.getTimeRange()).thenReturn(new TimeRange(now, now + 60_000*8));
        when(bucketConfig.getBuckets()).thenReturn(Buckets.fromStep(now + 60_000, now + 60_000 * 8, 60_000));
        List<NumericBucketPoint> actual = metricsService.findCounterStats(counter.getMetricId(),
                bucketConfig, asList(new Percentile("95.0"))).toBlocking().single();

        List<NumericBucketPoint> expected = new ArrayList<>();
        for (int i = 1; i < 8; i++) {
            NumericBucketPoint.Builder builder = new NumericBucketPoint.Builder(now + 60_000 * i, now + (60_000 * (i +
                    1)));
            switch (i) {
                case 1:
                    builder.setAvg(100D).setMax(200D).setMedian(0D).setMin(0D)
                            .setPercentiles(asList(new Percentile("95.0", 0D))).setSamples(2).setSum(200D);
                    break;
                case 2:
                case 4:
                case 6:
                    break;
                case 3:
                    builder.setAvg(400D).setMax(400D).setMedian(400D).setMin(400D)
                            .setPercentiles(asList(new Percentile("95.0", 400D))).setSamples(1).setSum(400D);
                    break;
                case 5:
                    builder.setAvg(550D).setMax(550D).setMedian(550D).setMin(550D)
                            .setPercentiles(asList(new Percentile("95.0", 550D))).setSamples(1).setSum(550D);
                    break;
                case 7:
                    builder.setAvg(975D).setMax(1000D).setMedian(950D).setMin(950D)
                            .setPercentiles(asList(new Percentile("95.0", 950D))).setSamples(2).setSum(1950D);
                    break;
            }
            expected.add(builder.build());
        }

        assertNumericBucketsEquals(actual, expected);
    }

    @Test
    public void findSimpleGaugeStatsByTags() {
        NumericDataPointCollector.createPercentile = InMemoryPercentileWrapper::new;

        String tenantId = "findGaugeStatsByTags";
        DateTime start = now().minusMinutes(10);

        Metric<Double> m1 = new Metric<>(new MetricId<>(tenantId, GAUGE, "M1"), asList(
                new DataPoint<>(start.getMillis(), 12.23),
                new DataPoint<>(start.plusMinutes(1).getMillis(), 9.745),
                new DataPoint<>(start.plusMinutes(2).getMillis(), 14.01),
                new DataPoint<>(start.plusMinutes(3).getMillis(), 16.18),
                new DataPoint<>(start.plusMinutes(4).getMillis(), 18.94)));
        doAction(() -> metricsService.addDataPoints(GAUGE, Observable.just(m1)));
        doAction(() -> metricsService.addTags(m1, ImmutableMap.of("type", "cpu_usage", "node", "server1")));

        Metric<Double> m2 = new Metric<>(new MetricId<>(tenantId, GAUGE, "M2"), asList(
                new DataPoint<>(start.getMillis(), 15.47),
                new DataPoint<>(start.plusMinutes(1).getMillis(), 8.08),
                new DataPoint<>(start.plusMinutes(2).getMillis(), 14.39),
                new DataPoint<>(start.plusMinutes(3).getMillis(), 17.76),
                new DataPoint<>(start.plusMinutes(4).getMillis(), 17.502)));
        doAction(() -> metricsService.addDataPoints(GAUGE, Observable.just(m2)));
        doAction(() -> metricsService.addTags(m2, ImmutableMap.of("type", "cpu_usage", "node", "server2")));

        Metric<Double> m3 = new Metric<>(new MetricId<>(tenantId, GAUGE, "M3"), asList(
                new DataPoint<>(start.getMillis(), 11.456),
                new DataPoint<>(start.plusMinutes(1).getMillis(), 18.32)));
        doAction(() -> metricsService.addDataPoints(GAUGE, Observable.just(m3)));
        doAction(() -> metricsService.addTags(m3, ImmutableMap.of("type", "cpu_usage", "node", "server3")));

        Buckets buckets = Buckets.fromCount(start.getMillis(), start.plusMinutes(5).getMillis(), 1);
        String tagFilters = "type:cpu_usage,node:server1|server2";

        List<List<NumericBucketPoint>> actual = getOnNextEvents(
                () -> metricsService.findMetricIdentifiersWithFilters(tenantId, GAUGE, tagFilters)
                        .toList()
                        .flatMap(ids -> metricsService.findNumericStats(ids, start.getMillis(), start.plusMinutes(5)
                                        .getMillis(), buckets, emptyList(), false, false)));

        assertEquals(actual.size(), 1);

        List<NumericBucketPoint> expected = Arrays.asList(createSingleBucket(
                Stream.concat(m1.getDataPoints().stream(), m2.getDataPoints().stream()).collect(Collectors.toList()),
                start, start.plusMinutes(5)));

        assertNumericBucketsEquals(actual.get(0), expected);
    }

    @Test
    public void findTaggedGaugeBucketPointsWithSimpleTagQuery() throws Exception {
        String tenantId = "tagged-bucket-points";
        DateTime start = now();
        DateTime end = start.plusHours(2);

        MetricId<Double> metricId = new MetricId<>(tenantId, GAUGE, "m1");
        Metric<Double> metric = new Metric<>(metricId, asList(
                new DataPoint<>(start.getMillis(), 27.43, ImmutableMap.of("x", "1")),
                new DataPoint<>(start.plusMinutes(5).getMillis(), 32.05, ImmutableMap.of("x", "2")),
                new DataPoint<>(start.plusMinutes(10).getMillis(), 34.14, ImmutableMap.of("x", "2")),
                new DataPoint<>(start.plusMinutes(15).getMillis(), 29.73, ImmutableMap.of("x", "1")),
                new DataPoint<>(start.plusMinutes(20).getMillis(), 28.44, ImmutableMap.of("x", "3")),
                new DataPoint<>(start.plusMinutes(25).getMillis(), 51.91, ImmutableMap.of("x", "3"))));
        doAction(() -> metricsService.addDataPoints(GAUGE, Observable.just(metric)));

        Map<String, TaggedBucketPoint> actual = getOnNextEvents(
                () -> metricsService.findGaugeStats(metricId, ImmutableMap.of("x", "*"), start.getMillis(),
                        end.getMillis(), emptyList())).get(0);

        assertEquals(actual.size(), 3, "The number of buckets do not match");

        TaggedDataPointCollector collector = new TaggedDataPointCollector(ImmutableMap.of("x", "1"), emptyList());
        collector.increment(metric.getDataPoints().get(0));
        collector.increment(metric.getDataPoints().get(3));
        assertEquals(actual.get("x:1"), collector.toBucketPoint());

        collector = new TaggedDataPointCollector(ImmutableMap.of("x", "2"), emptyList());
        collector.increment(metric.getDataPoints().get(1));
        collector.increment(metric.getDataPoints().get(2));
        assertEquals(actual.get("x:2"), collector.toBucketPoint());

        collector = new TaggedDataPointCollector(ImmutableMap.of("x", "3"), emptyList());
        collector.increment(metric.getDataPoints().get(4));
        collector.increment(metric.getDataPoints().get(5));
        assertEquals(actual.get("x:3"), collector.toBucketPoint());
    }

    @Test
    public void findTaggedGaugeBucketPointsWithMultipleTagFilters() throws Exception {
        String tenantId = "multiple-tag-filter-data-point-query";
        DateTime start = now();
        DateTime end = start.plusHours(2);

        MetricId<Double> metricId = new MetricId<>(tenantId, GAUGE, "m1");
        Metric<Double> metric = new Metric<>(metricId, asList(
                new DataPoint<>(start.getMillis(), 11.1, ImmutableMap.of("x", "1", "y", "1", "z", "1")),
                new DataPoint<>(start.plusMinutes(2).getMillis(), 13.3, ImmutableMap.of("x", "2", "y", "2", "z", "2")),
                new DataPoint<>(start.plusMinutes(4).getMillis(), 14.4, ImmutableMap.of("x", "3", "y", "2", "z", "3")),
                new DataPoint<>(start.plusMinutes(6).getMillis(), 15.5,
                        ImmutableMap.of("x", "1", "y", "3", "z", "4"))));
        doAction(() -> metricsService.addDataPoints(GAUGE, Observable.just(metric)));

        Map<String, TaggedBucketPoint> actual = getOnNextEvents(
                () -> metricsService.findGaugeStats(metricId, ImmutableMap.of("x", "*", "y", "2", "z", "2|3"),
                        start.getMillis(), end.getMillis(), Collections.emptyList())).get(0);
        assertEquals(actual.size(), 2);

        TaggedDataPointCollector collector = new TaggedDataPointCollector(
                ImmutableMap.of("x", "2", "y", "2", "z", "2"),
                emptyList());
        collector.increment(metric.getDataPoints().get(1));
        assertEquals(actual.get("x:2,y:2,z:2"), collector.toBucketPoint());

        collector = new TaggedDataPointCollector(ImmutableMap.of("x", "3", "y", "2", "z", "3"), emptyList());
        collector.increment(metric.getDataPoints().get(2));
        assertEquals(actual.get("x:3,y:2,z:3"), collector.toBucketPoint());
    }

    @Test
    public void findTaggedCounterBucketPointsWithMultipleTagFilters() throws Exception {
        String tenantId = "multiple-tag-filter-counter-data-point-query";
        DateTime start = now();
        DateTime end = start.plusHours(2);

        MetricId<Long> metricId = new MetricId<>(tenantId, COUNTER, "C1");
        Metric<Long> metric = new Metric<>(metricId, asList(
                new DataPoint<>(start.getMillis(), 11L, ImmutableMap.of("x", "1", "y", "1", "z", "1")),
                new DataPoint<>(start.plusMinutes(2).getMillis(), 13L, ImmutableMap.of("x", "2", "y", "2", "z", "2")),
                new DataPoint<>(start.plusMinutes(4).getMillis(), 14L, ImmutableMap.of("x", "3", "y", "2", "z", "3")),
                new DataPoint<>(start.plusMinutes(6).getMillis(), 15L,
                        ImmutableMap.of("x", "1", "y", "3", "z", "4"))));
        doAction(() -> metricsService.addDataPoints(COUNTER, Observable.just(metric)));

        Map<String, TaggedBucketPoint> actual = getOnNextEvents(
                () -> metricsService.findCounterStats(metricId, ImmutableMap.of("x", "*", "y", "2", "z", "2|3"),
                        start.getMillis(), end.getMillis(), Collections.emptyList())).get(0);
        assertEquals(actual.size(), 2);

        TaggedDataPointCollector collector = new TaggedDataPointCollector(
                ImmutableMap.of("x", "2", "y", "2", "z", "2"),
                emptyList());
        collector.increment(metric.getDataPoints().get(1));
        assertEquals(actual.get("x:2,y:2,z:2"), collector.toBucketPoint());

        collector = new TaggedDataPointCollector(ImmutableMap.of("x", "3", "y", "2", "z", "3"), emptyList());
        collector.increment(metric.getDataPoints().get(2));
        assertEquals(actual.get("x:3,y:2,z:3"), collector.toBucketPoint());
    }

}

redpajama_set_name: RedPajamaGithub


text:
<?php

require_once('Controllers/Example.php');
require_once('Controllers/Exceptional.php');
require_once('Controllers/Whoops.php');
require_once('Controllers/AccessDenied.php');

class JAPITest extends PHPUnit_Framework_TestCase
{

    /**
     * Test the dispatch() cycle
     */
    public function testDispatchCycle()
    {
        // Mocked controller & expectations
        $obj_controller = $this->getMockBuilder('\\Example')->getMock();
        $obj_controller->expects($this->once())->method('preDispatch');
        $obj_controller->expects($this->once())->method('dispatch');
        $obj_controller->expects($this->once())->method('postDispatch');

        // Mock JAPI (just replace the sendResponse method to avoid output errors)
        $obj_japi = $this->getMockBuilder('\\Docnet\\JAPI')->setMethods(['sendResponse'])->getMock();

        // Dispatch
        $obj_japi->dispatch($obj_controller);
    }

    /**
     * Validate the bootstrap() cycle works on a supplied Controller
     */
    public function testConcreteBootstrapCycle()
    {
        // Mocked controller & expectations
        $obj_controller = $this->getMockBuilder('\\Example')->getMock();
        $obj_controller->expects($this->once())->method('preDispatch');
        $obj_controller->expects($this->once())->method('dispatch');
        $obj_controller->expects($this->once())->method('postDispatch');

        // Mock JAPI (just replace the sendResponse method to avoid output errors)
        $obj_japi = $this->getMockBuilder('\\Docnet\\JAPI')->setMethods(['sendResponse'])->getMock();
        $obj_japi->expects($this->once())->method('sendResponse');

        // Dispatch
        $obj_japi->bootstrap($obj_controller);
    }

    /**
     * Test the bootstrap() methods correctly executes the supplied callback
     *
     * @todo Implement this test!
     */
    public function testBootstrapCallback()
    {
    }

    /**
     * Test Exceptions are correctly passed to jsonError from the bootstrap() method
     */
    public function testBootstrapErrorCycle()
    {
        // Mock JAPI
        $obj_japi = $this->getMockBuilder('\\Docnet\\JAPI')->setMethods(['sendResponse', 'jsonError'])->getMock();
        $obj_japi->expects($this->never())->method('sendResponse');
        $obj_japi->expects($this->once())->method('jsonError')->with(
            $this->equalTo(new Exception()),
            $this->equalTo(0)
        );

        // Dispatch
        $obj_japi->bootstrap(new Whoops());
    }

    /**
     * Test custom Exception codes are correctly passed to jsonError from the bootstrap() method
     */
    public function testBootstrapCustomErrorCycle()
    {
        // Mock JAPI
        $obj_japi = $this->getMockBuilder('\\Docnet\\JAPI')->setMethods(['sendResponse', 'jsonError'])->getMock();
        $obj_japi->expects($this->never())->method('sendResponse');
        $obj_japi->expects($this->once())->method('jsonError')->with(
            $this->equalTo(new RuntimeException('Error Message', 400)),
            $this->equalTo(400)
        );

        // Dispatch
        $obj_japi->bootstrap(new Exceptional());
    }

    /**
     * Validate the response data from the Controller is correctly passed to sendResponse()
     */
    public function testSendResponse()
    {
        // Mock JAPI
        $obj_japi = $this->getMockBuilder('\\Docnet\\JAPI')->setMethods(['sendResponse'])->getMock();
        $obj_japi->expects($this->once())->method('sendResponse')->with(
            $this->equalTo(['test' => TRUE]),
            $this->equalTo(200)
        );

        // Dispatch
        $obj_japi->bootstrap(new Example());
    }

    /**
     * Do we correctly call the logger in jsonError scenarios?
     */
    public function testLogger()
    {
        // Mock the logger
        $obj_logger = $this->getMockBuilder('\\Psr\\Log\\NullLogger')->setMethods(['log'])->getMock();
        $obj_logger->expects($this->once())->method('log')->with(
            $this->equalTo(\Psr\Log\LogLevel::ERROR)
        );

        // Mock JAPI
        $obj_japi = $this->getMockBuilder('\\Docnet\\JAPI')->setMethods(['sendResponse'])->getMock();
        $obj_japi->setLogger($obj_logger);

        // Dispatch
        $obj_japi->bootstrap(new Exceptional());
    }

    /**
     * Ensure we do not fall over when no logger has been supplied
     */
    public function testNoLogger()
    {
        // Mock JAPI
        $obj_japi = $this->getMockBuilder('\\Docnet\\JAPI')->setMethods(['sendResponse'])->getMock();

        // Dispatch
        $obj_japi->bootstrap(new Exceptional());
    }

    /**
     * Test an AccessDenied Exception codes are correctly passed to jsonError from the bootstrap() method
     */
    public function testBootstrapAccessDeniedErrorCycle()
    {
        // Mock JAPI
        $obj_japi = $this->getMockBuilder('\\Docnet\\JAPI')->setMethods(['sendResponse', 'jsonError'])->getMock();
        $obj_japi->expects($this->never())->method('sendResponse');
        $obj_japi->expects($this->once())->method('jsonError')->with(
            $this->equalTo(new \Docnet\JAPI\Exceptions\AccessDenied('Error Message', 403)),
            $this->equalTo(403)
        );

        // Dispatch
        $obj_japi->bootstrap(new AccessDenied());
    }

}


text:
svnX
====

This is the updated version of https://code.google.com/p/svnx/ since the auther does not update it anymore.


text:
# Abacus Machine Simulator
Gaurav Manek, 2016

## Attribution
This work is released under [The MIT License](./LICENSE). It uses a parser generated by [PEG.js](http://pegjs.org/) for parsing, and [CodeMirror](http://codemirror.net/) with packages for syntax highlighting.

This work was made possible by [Prof. Richard Heck](http://rgheck.frege.org/), who has graciously given direction and time to this project.

A big thank you to [Prof. Shriram Krishnamurthi](https://cs.brown.edu/~sk/) for advice in the design of the language.

## Language
The `abacusmachine` language is desgined to be a simple and effective language for specifying abacus machines. It comes with built-in testing and scope features to make it easier to build complex functions and verify code correctness.

### Control Flow
Control flow proceeds sequentially forwards unless changed by a `goto` or branching within a register command. Jump anchors are specified at the start of a line with a leading `:`. All names must begin with a letter and can include letters, numbers, and `_`. Examples are:
```
	:anchor_name
	:this123 /* code continues on this line */
```

Within register commands, `next` can be used to refer to the next line instead of an anchor. You cannot place an anchor on the same line as a `goto`.

Runtime detection of infinite loops has been implemented, so some types of infinite loops will stop the engine automatically.

### Registers
As with any abacus machine, an unlimited number of registers storing natural numbers are available. Registers are always specified by square brackets, and may be defined numerically (`[1]`, `[5]`, etc.) or by name (`[ret_val]`, `[arg1]`, etc.). Registers contain the value 0 by default.

Two operations can be performed on registers, addition and subtraction. They are written as:
```

  [7]+; // Increment register 1 and continue.
  [7]-; // Decrement register 1 and continue.
  [7]+, next;         // Increment and go to the next line.
  [7]-, :start, next; // Decrement, and if the register is
                      //  still positive go to :start. If
                      //  zero, go to the next line.
```

### Functions
All executable code is organized into functions, each with its own scope. The functions are written as:
```
function rem([1],[arg2]) -> [rv1], [7] {
   /* code omitted */
} where {
  rem(5,7) is rem(12,7);
  rem(10,6) is 4;
}
```

Where `rem` is the function name, the function arguments are given as a comma-separated list of registers. The registers containing the return value are specified after the `->`. At the end of the function whatever the value stored in the return registers is passed to the calling function.

Function calls are made as follows: `quo([1], [2]) -> [6], [8];`. Registers in the parenthesis are passed by value to the function, and the return values are copied to the source registers, overwriting whatever previous values were stored.

### Recursion
In actual abacus machines the function abstraction does not exist, and functions are implemented by "copying" the structure of the abacus machine into each place the function is invoked. To ensure that this language does not (accidentally) allow for an abacus machine with infinite states to be implemented, recursive calls (including mutual recursion) are not allowed.

### Testing
Tests are built into the language to encourage good testing practices. A `where` block at the end of a function contains tests of the form `name(1, 2, 3) is 2, 3` or `name(1, 2) is name(4, 5)`. The left-hand side must be a function call and the right-hand side can be a list of integers or another function call. The `is` operator is checks for equality: a test is only considered passed if the output of the function on the left and right (or the values on the right) are of equal length and value.

## Features

### Automatic Saving of Code
Each time code is updated, it is automatically saved by the platform. To restore the starting code, delete everything in the editor window, wait for at least two seconds, then refresh the window.

### Testing

Upon a successful compilation, tests are automatically run, and the result of each test is displayed in the gutter next to the test. Hovering over the gutter will show the result of evaluation underneath the line. Function tests are automatically run in *dependency order*, where function `a` is evaluated before `b` if `b` depends on `a`. Testing may be stopped early if some types of errors are encountered, so this may be useful under some circumstances.

### Debugger

Upon a successful compilation, the pane on the left is replaced by the debugger pane, which can be used to examine the abacus machine as it is executing. Enter a function call in the toolbar at the top of the pane and click "Load and Pause" (or press `Enter`) to load the function in the debugger. Once loaded, you can step through the function in four different ways:
  - Run to End (`Ctrl+E`), to run the function until it returns.
  - Step Over (`Ctrl+,`), to take one step, not descending into function calls.
  - Step Into (`Ctrl+.`), to take one step, descending into function calls.
  - Step Out (`Ctrl+/`), to run until the current function returns.

Each time you take a step in a function, the next line to be executed is highlighted.

When execution is paused, the stack is displayed at the bottom of the debugger pane, and you can examine the state of the machine at different levels of the stack by clicking on different stack frames. When examining any stack frame, you can see the values of all registers in the function in the state area above the stack.

When examining the current stack frame, you can edit the value of the register stored there in the state view. The change takes effect immediately, so the next step executes with the changed value.

In addition to these options, you can use breakpoints to pause execution and inspect the machine during execution:

### Breakpoints

Clicking on the gutter next to a line will set a breakpoint on that line. When running code from the debugger, execution is paused when it reaches a breakpoint. Breakpoints may be set at the head or within the body of a function, but not within empty space or a where block.

Before code is compiled, breakpoints are set in a blue color and may be set at any line. Upon compilation, breakpoints positions are recalculated and only valid breakpoints (those that meet the above conditions) are kept and colored red.

Breakpoints set at a function header will stop execution just before a function returns. Breakpoints set within a function will stop execution so that the highlighted line is the next to be executed.

### Static & Dynamic Analysis

At compile-time some static analysis is automatically performed, and the compiler reports functions where the exit is unreachable, infinite `goto` loops, and unreachable lines.

At runtime further analysis is performed, including recursion detection and branch-free loops.

## Open Source

### Issues

Feel free to report bugs and request features by creating a [new issue](https://github.com/gauravmm/AbacusMachineSim/issues) and tagging it appropriately.

If you are reporting a bug with the code, please include a minimum working example. If you are reporting a bug with the design/style, please include a screenshot and your OS, browser, and version. (Note that we only support IE9 or better.)

### Contribution

Please only contribute your own work, and only if you are willing to release it under the license of this work.

Branch management is done via [gitflow](http://nvie.com/posts/a-successful-git-branching-model/). Please fork off `develop` and target all pull requests to the same. Feature branches should be named `feature-name` and hotfixes `hotfix-name`.