text:
import time

from six.moves import range as xrange

from tempest.common.utils import data_utils
from tempest.lib import exceptions
from tempest import test

from monasca_tempest_tests.tests.api import base
from monasca_tempest_tests.tests.api import constants
from monasca_tempest_tests.tests.api import helpers

class TestMetrics(base.BaseMonascaTest):

    @classmethod
    def resource_setup(cls):
        super(TestMetrics, cls).resource_setup()

    @classmethod
    def resource_cleanup(cls):
        super(TestMetrics, cls).resource_cleanup()

    @test.attr(type='gate')
    def test_create_metric(self):
        name = data_utils.rand_name('name')
        key = data_utils.rand_name('key')
        value = data_utils.rand_name('value')
        timestamp = int(round(time.time() * 1000))
        time_iso = helpers.timestamp_to_iso(timestamp)
        end_timestamp = int(round((time.time() + 3600 * 24) * 1000))
        end_time_iso = helpers.timestamp_to_iso(end_timestamp)
        value_meta_key = data_utils.rand_name('value_meta_key')
        value_meta_value = data_utils.rand_name('value_meta_value')
        metric = helpers.create_metric(name=name,
                                       dimensions={key: value},
                                       timestamp=timestamp,
                                       value=1.23,
                                       value_meta={
                                           value_meta_key: value_meta_value
                                       })
        resp, response_body = self.monasca_client.create_metrics(metric)
        self.assertEqual(204, resp.status)
        query_param = '?name=' + name + '&start_time=' + time_iso + \
                      '&end_time=' + end_time_iso
        for i in xrange(constants.MAX_RETRIES):
            resp, response_body = self.monasca_client.\
                list_measurements(query_param)
            self.assertEqual(200, resp.status)
            elements = response_body['elements']
            for element in elements:
                if str(element['name']) == name:
                    self._verify_list_measurements_element(element, key, value)
                    measurement = element['measurements'][0]
                    self._verify_list_measurements_measurement(
                        measurement, metric, value_meta_key, value_meta_value)
                    return
            time.sleep(constants.RETRY_WAIT_SECS)
            if i == constants.MAX_RETRIES - 1:
                error_msg = "Failed test_create_metric: " \
                            "timeout on waiting for metrics: at least " \
                            "one metric is needed. Current number of " \
                            "metrics = 0"
                self.fail(error_msg)

    @test.attr(type='gate')
    def test_create_metrics(self):
        name = data_utils.rand_name('name')
        key = data_utils.rand_name('key')
        value = data_utils.rand_name('value')
        timestamp = int(round(time.time() * 1000))
        time_iso = helpers.timestamp_to_iso(timestamp)
        end_timestamp = int(round(timestamp + 3600 * 24 * 1000))
        end_time_iso = helpers.timestamp_to_iso(end_timestamp)
        value_meta_key1 = data_utils.rand_name('meta_key')
        value_meta_value1 = data_utils.rand_name('meta_value')
        value_meta_key2 = data_utils.rand_name('value_meta_key')
        value_meta_value2 = data_utils.rand_name('value_meta_value')
        metrics = [
            helpers.create_metric(name=name,
                                  dimensions={key: value},
                                  timestamp=timestamp,
                                  value=1.23,
                                  value_meta={
                                      value_meta_key1: value_meta_value1
                                  }),
            helpers.create_metric(name=name,
                                  dimensions={key: value},
                                  timestamp=timestamp + 6000,
                                  value=4.56,
                                  value_meta={
                                      value_meta_key2: value_meta_value2
                                  })
        ]
        resp, response_body = self.monasca_client.create_metrics(metrics)
        self.assertEqual(204, resp.status)
        query_param = '?name=' + name + '&start_time=' + str(time_iso) + \
                      '&end_time=' + str(end_time_iso)
        for i in xrange(constants.MAX_RETRIES):
            resp, response_body = self.monasca_client.\
                list_measurements(query_param)
            self.assertEqual(200, resp.status)
            elements = response_body['elements']
            for element in elements:
                if str(element['name']) == name \
                        and len(element['measurements']) == 2:
                    self._verify_list_measurements_element(element, key, value)
                    first_measurement = element['measurements'][0]
                    second_measurement = element['measurements'][1]
                    self._verify_list_measurements_measurement(
                        first_measurement, metrics[0], value_meta_key1,
                        value_meta_value1)
                    self._verify_list_measurements_measurement(
                        second_measurement, metrics[1], value_meta_key2,
                        value_meta_value2)
                    return
            time.sleep(constants.RETRY_WAIT_SECS)
            if i == constants.MAX_RETRIES - 1:
                error_msg = "Failed test_create_metrics: " \
                            "timeout on waiting for metrics: at least " \
                            "one metric is needed. Current number of " \
                            "metrics = 0"
                self.fail(error_msg)

    @test.attr(type='gate')
    @test.attr(type=['negative'])
    def test_create_metric_with_no_name(self):
        metric = helpers.create_metric(name=None)
        self.assertRaises(exceptions.UnprocessableEntity,
                          self.monasca_client.create_metrics,
                          metric)

    @test.attr(type='gate')
    @test.attr(type=['negative'])
    def test_create_metric_with_empty_name(self):
        metric = helpers.create_metric(name='')
        self.assertRaises(exceptions.UnprocessableEntity,
                          self.monasca_client.create_metrics,
                          metric)

    @test.attr(type='gate')
    @test.attr(type=['negative'])
    def test_create_metric_with_empty_value_in_dimensions(self):
        name = data_utils.rand_name('name')
        metric = helpers.create_metric(name=name,
                                       dimensions={'key': ''})
        self.assertRaises(exceptions.UnprocessableEntity,
                          self.monasca_client.create_metrics,
                          metric)

    @test.attr(type='gate')
    @test.attr(type=['negative'])
    def test_create_metric_with_empty_key_in_dimensions(self):
        name = data_utils.rand_name('name')
        metric = helpers.create_metric(name=name,
                                       dimensions={'': 'value'})
        self.assertRaises(exceptions.UnprocessableEntity,
                          self.monasca_client.create_metrics,
                          metric)

    @test.attr(type='gate')
    def test_create_metric_with_no_dimensions(self):
        name = data_utils.rand_name('name')
        timestamp = int(round(time.time() * 1000))
        time_iso = helpers.timestamp_to_iso(timestamp)
        end_timestamp = int(round(timestamp + 3600 * 24 * 1000))
        end_time_iso = helpers.timestamp_to_iso(end_timestamp)
        value_meta_key = data_utils.rand_name('value_meta_key')
        value_meta_value = data_utils.rand_name('value_meta_value')
        metric = helpers.create_metric(name=name,
                                       dimensions=None,
                                       timestamp=timestamp,
                                       value=1.23,
                                       value_meta={
                                           value_meta_key: value_meta_value})
        resp, response_body = self.monasca_client.create_metrics(metric)
        self.assertEqual(204, resp.status)
        query_param = '?name=' + str(name) + '&start_time=' + str(time_iso) \
                      + '&end_time=' + str(end_time_iso)
        for i in xrange(constants.MAX_RETRIES):
            resp, response_body = self.monasca_client.\
                list_measurements(query_param)
            self.assertEqual(200, resp.status)
            elements = response_body['elements']
            for element in elements:
                if str(element['name']) == name:
                    self._verify_list_measurements_element(
                        element, test_key=None, test_value=None)
                    if len(element['measurements']) > 0:
                        measurement = element['measurements'][0]
                        self._verify_list_measurements_measurement(
                            measurement, metric, value_meta_key,
                            value_meta_value)
                        return
            time.sleep(constants.RETRY_WAIT_SECS)
            if i == constants.MAX_RETRIES - 1:
                error_msg = "Failed test_create_metric_with_no_dimensions: " \
                            "timeout on waiting for metrics: at least " \
                            "one metric is needed. Current number of " \
                            "metrics = 0"
                self.fail(error_msg)

    @test.attr(type='gate')
    @test.attr(type=['negative'])
    def test_create_metric_with_no_timestamp(self):
        metric = helpers.create_metric()
        metric['timestamp'] = None
        self.assertRaises(exceptions.UnprocessableEntity,
                          self.monasca_client.create_metrics,
                          metric)

    @test.attr(type='gate')
    @test.attr(type=['negative'])
    def test_create_metric_no_value(self):
        timestamp = int(round(time.time() * 1000))
        metric = helpers.create_metric(timestamp=timestamp,
                                       value=None)
        self.assertRaises(exceptions.UnprocessableEntity,
                          self.monasca_client.create_metrics,
                          metric)

    @test.attr(type='gate')
    @test.attr(type=['negative'])
    def test_create_metric_with_name_exceeds_max_length(self):
        long_name = "x" * (constants.MAX_METRIC_NAME_LENGTH + 1)
        metric = helpers.create_metric(long_name)
        self.assertRaises(exceptions.UnprocessableEntity,
                          self.monasca_client.create_metrics,
                          metric)

    @test.attr(type='gate')
    @test.attr(type=['negative'])
    def test_create_metric_with_invalid_chars_in_name(self):
        for invalid_char in constants.INVALID_CHARS:
            metric = helpers.create_metric(invalid_char)
            self.assertRaises(exceptions.UnprocessableEntity,
                              self.monasca_client.create_metrics,
                              metric)

    @test.attr(type='gate')
    @test.attr(type=['negative'])
    def test_create_metric_with_invalid_chars_in_dimensions(self):
        for invalid_char in constants.INVALID_CHARS:
            metric = helpers.create_metric('name-1', {'key-1': invalid_char})
            self.assertRaises(exceptions.UnprocessableEntity,
                              self.monasca_client.create_metrics,
                              metric)
        for invalid_char in constants.INVALID_CHARS:
            metric = helpers.create_metric('name-1', {invalid_char: 'value-1'})
            self.assertRaises(exceptions.UnprocessableEntity,
                              self.monasca_client.create_metrics,
                              metric)

    @test.attr(type='gate')
    @test.attr(type=['negative'])
    def test_create_metric_dimension_key_exceeds_max_length(self):
        long_key = "x" * (constants.MAX_DIMENSION_KEY_LENGTH + 1)
        metric = helpers.create_metric('name-1', {long_key: 'value-1'})
        self.assertRaises(exceptions.UnprocessableEntity,
                          self.monasca_client.create_metrics,
                          metric)

    @test.attr(type='gate')
    @test.attr(type=['negative'])
    def test_create_metric_dimension_value_exceeds_max_length(self):
        long_value = "x" * (constants.MAX_DIMENSION_VALUE_LENGTH + 1)
        metric = helpers.create_metric('name-1', {'key-1': long_value})
        self.assertRaises(exceptions.UnprocessableEntity,
                          self.monasca_client.create_metrics,
                          metric)

    @test.attr(type='gate')
    @test.attr(type=['negative'])
    def test_create_metric_with_value_meta_name_exceeds_max_length(self):
        long_value_meta_name = "x" * (constants.MAX_VALUE_META_NAME_LENGTH + 1)
        value_meta_dict = {long_value_meta_name: "value_meta_value"}
        metric = helpers.create_metric(name='name', value_meta=value_meta_dict)
        self.assertRaises(exceptions.UnprocessableEntity,
                          self.monasca_client.create_metrics,
                          metric)

    @test.attr(type='gate')
    @test.attr(type=['negative'])
    def test_create_metric_with_value_meta_exceeds_max_length(self):
        value_meta_name = "x"
        long_value_meta_value = "y" * constants.MAX_VALUE_META_TOTAL_LENGTH
        value_meta_dict = {value_meta_name: long_value_meta_value}
        metric = helpers.create_metric(name='name', value_meta=value_meta_dict)
        self.assertRaises(exceptions.UnprocessableEntity,
                          self.monasca_client.create_metrics,
                          metric)

    @test.attr(type='gate')
    def test_list_metrics(self):
        resp, response_body = self.monasca_client.list_metrics()
        self.assertEqual(200, resp.status)
        self.assertTrue(set(['links', 'elements']) == set(response_body))
        elements = response_body['elements']
        element = elements[0]
        self._verify_list_metrics_element(element, test_key=None,
                                          test_value=None, test_name=None)
        self.assertTrue(set(['id', 'name', 'dimensions']) == set(element))

    @test.attr(type='gate')
    def test_list_metrics_with_dimensions(self):
        name = data_utils.rand_name('name')
        key = data_utils.rand_name('key')
        value = data_utils.rand_name('value')
        metric = helpers.create_metric(name=name, dimensions={key: value})
        resp, response_body = self.monasca_client.create_metrics(metric)
        self.assertEqual(204, resp.status)
        query_param = '?dimensions=' + key + ':' + value
        for i in xrange(constants.MAX_RETRIES):
            resp, response_body = self.monasca_client.list_metrics(query_param)
            self.assertEqual(200, resp.status)
            elements = response_body['elements']
            for element in elements:
                if str(element['dimensions'][key]) == value:
                    self._verify_list_metrics_element(element, test_name=name)
                    return
            time.sleep(constants.RETRY_WAIT_SECS)
            if i == constants.MAX_RETRIES - 1:
                error_msg = "Failed test_list_metrics_with_dimensions: " \
                            "timeout on waiting for metrics: at least " \
                            "one metric is needed. Current number of " \
                            "metrics = 0"
                self.fail(error_msg)

    @test.attr(type='gate')
    def test_list_metrics_dimension_query_multi_value_with_diff_names(self):
        metrics, name, key_service, values = \
            self._create_metrics_with_different_dimensions(same_name=False)
        metric_dimensions = self._get_metric_dimensions(
            key_service, values, same_metric_name=False)
        query_param = '?dimensions=' + key_service + ':' + values[0] + '|' +\
                      values[1]
        self._verify_dimensions(query_param, metric_dimensions)

    @test.attr(type='gate')
    def test_list_metrics_dimension_query_no_value_with_diff_names(self):
        metrics, name, key_service, values = \
            self._create_metrics_with_different_dimensions(same_name=False)
        metric_dimensions = self._get_metric_dimensions(
            key_service, values, same_metric_name=False)
        query_param = '?dimensions=' + key_service
        self._verify_dimensions(query_param, metric_dimensions)

    @test.attr(type='gate')
    def test_list_metrics_dimension_query_multi_value_with_same_name(self):
        # Skip the test for now due to InfluxDB Inconsistency
        return
        metrics, name, key_service, values = \
            self._create_metrics_with_different_dimensions(same_name=True)
        metric_dimensions = self._get_metric_dimensions(
            key_service, values, same_metric_name=True)
        query_param = '?name=' + name + '&dimensions=' + key_service + ':' +\
                      values[0] + '|' + values[1]
        self._verify_dimensions(query_param, metric_dimensions)

    @test.attr(type='gate')
    def test_list_metrics_dimension_query_no_value_with_same_name(self):
        # Skip the test for now due to InfluxDB Inconsistency
        return
        metrics, name, key_service, values = \
            self._create_metrics_with_different_dimensions(same_name=True)
        metric_dimensions = self._get_metric_dimensions(
            key_service, values, same_metric_name=True)
        query_param = '?name=' + name + '&dimensions=' + key_service
        self._verify_dimensions(query_param, metric_dimensions)

    @test.attr(type='gate')
    def test_list_metrics_with_name(self):
        name = data_utils.rand_name('name')
        key = data_utils.rand_name('key')
        value = data_utils.rand_name('value')
        metric = helpers.create_metric(name=name,
                                       dimensions={key: value})
        resp, response_body = self.monasca_client.create_metrics(metric)
        self.assertEqual(204, resp.status)
        query_param = '?name=' + str(name)
        for i in xrange(constants.MAX_RETRIES):
            resp, response_body = self.monasca_client.list_metrics(query_param)
            self.assertEqual(200, resp.status)
            elements = response_body['elements']
            for element in elements:
                if str(element['name']) == name:
                    self._verify_list_metrics_element(element, test_key=key,
                                                      test_value=value)
                    return
            time.sleep(constants.RETRY_WAIT_SECS)
            if i == constants.MAX_RETRIES - 1:
                error_msg = "Failed test_list_metrics_with_name: " \
                            "timeout on waiting for metrics: at least " \
                            "one metric is needed. Current number of " \
                            "metrics = 0"
                self.fail(error_msg)

    @test.attr(type='gate')
    def test_list_metrics_with_tenant(self):
        name = data_utils.rand_name('name')
        key = data_utils.rand_name('key')
        value = data_utils.rand_name('value')
        tenant = self.tenants_client.create_tenant(
            name=data_utils.rand_name('test_tenant'))['tenant']
        # Delete the tenant at the end of the test
        self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
        metric = helpers.create_metric(name=name,
                                       dimensions={key: value})
        resp, response_body = self.monasca_client.create_metrics(
            metric, tenant_id=tenant['id'])
        self.assertEqual(204, resp.status)
        query_param = '?tenant_id=' + str(tenant['id'])
        for i in xrange(constants.MAX_RETRIES):
            resp, response_body = self.monasca_client.list_metrics(query_param)
            self.assertEqual(200, resp.status)
            elements = response_body['elements']
            for element in elements:
                if str(element['name']) == name:
                    self._verify_list_metrics_element(element, test_key=key,
                                                      test_value=value)
                    return
            time.sleep(constants.RETRY_WAIT_SECS)
            if i == constants.MAX_RETRIES - 1:
                error_msg = "Failed test_list_metrics_with_tenant: " \
                            "timeout on waiting for metrics: at least " \
                            "one metric is needed. Current number of " \
                            "metrics = 0"
                self.fail(error_msg)

    @test.attr(type='gate')
    def test_list_metrics_with_offset_limit(self):
        name = data_utils.rand_name()
        key1 = data_utils.rand_name()
        key2 = data_utils.rand_name()

        metrics = [
            helpers.create_metric(name=name, dimensions={
                key1: 'value-1', key2: 'value-1'}),
            helpers.create_metric(name=name, dimensions={
                key1: 'value-2', key2: 'value-2'}),
            helpers.create_metric(name=name, dimensions={
                key1: 'value-3', key2: 'value-3'}),
            helpers.create_metric(name=name, dimensions={
                key1: 'value-4', key2: 'value-4'})
        ]
        self.monasca_client.create_metrics(metrics)
        query_param = '?name=' + name
        for i in xrange(constants.MAX_RETRIES):
            resp, response_body = self.monasca_client.list_metrics(query_param)
            elements = response_body['elements']
            if elements and len(elements) == 4:
                break
            time.sleep(constants.RETRY_WAIT_SECS)
            if i == constants.MAX_RETRIES - 1:
                error_msg = ("Failed test_list_metrics_with_offset_limit: "
                             "timeout on waiting for metrics: 4 metrics "
                             "are needed. Current number of elements = "
                             "{}").format(len(elements))
                self.fail(error_msg)

        first_element = elements[0]
        query_parms = '?name=' + name + '&limit=4'
        resp, response_body = self.monasca_client.list_metrics(query_parms)
        self.assertEqual(200, resp.status)
        elements = response_body['elements']
        self.assertEqual(4, len(elements))
        self.assertEqual(first_element, elements[0])

        for metric_index in xrange(len(elements) - 1):
            metric = elements[metric_index]
            max_limit = 3 - metric_index

            for limit in xrange(1, max_limit):
                first_index = metric_index + 1
                last_index = first_index + limit
                expected_elements = elements[first_index:last_index]

                query_parms = '?name=' + name + '&offset=' + \
                              str(metric['id']) + '&limit=' + \
                              str(limit)
                resp, response_body = self.\
                    monasca_client.list_metrics(query_parms)
                self.assertEqual(200, resp.status)
                new_elements = response_body['elements']

                self.assertEqual(limit, len(new_elements))
                for i in xrange(len(expected_elements)):
                    self.assertEqual(expected_elements[i], new_elements[i])

    def _verify_list_measurements_element(self, element, test_key, test_value):
        self.assertEqual(set(element),
                         set(['columns', 'dimensions', 'id', 'measurements',
                             'name']))
        self.assertEqual(set(element['columns']),
                         set(['timestamp', 'value', 'value_meta']))
        self.assertTrue(str(element['id']) is not None)
        if test_key is not None and test_value is not None:
            self.assertEqual(str(element['dimensions'][test_key]), test_value)

    def _verify_list_measurements_measurement(self, measurement,
                                              test_metric, test_vm_key,
                                              test_vm_value):
        # Timestamps stored in influx sometimes are 1 millisecond different to
        # the value stored by the persister. Check if the timestamps are
        # equal in one millisecond range to pass the test.
        time_iso_millis = helpers.timestamp_to_iso_millis(
            test_metric['timestamp'] + 0)
        time_iso_millis_plus = helpers.timestamp_to_iso_millis(
            test_metric['timestamp'] + 1)
        time_iso_millis_minus = helpers.timestamp_to_iso_millis(
            test_metric['timestamp'] - 1)
        if str(measurement[0]) != time_iso_millis and str(measurement[0]) != \
                time_iso_millis_plus and str(measurement[0]) != \
                time_iso_millis_minus:
            error_msg = ("Mismatch Error: None of {}, {}, {} matches {}").\
                format(time_iso_millis, time_iso_millis_plus,
                       time_iso_millis_minus, str(measurement[0]))
            self.fail(error_msg)
        self.assertEqual(measurement[1], test_metric['value'])
        if test_vm_key is not None and test_vm_value is not None:
            self.assertEqual(str(measurement[2][test_vm_key]), test_vm_value)

    def _verify_list_metrics_element(self, element, test_key=None,
                                     test_value=None, test_name=None):
        self.assertTrue(type(element['id']) is unicode)
        self.assertTrue(type(element['name']) is unicode)
        self.assertTrue(type(element['dimensions']) is dict)
        self.assertEqual(set(element), set(['dimensions', 'id', 'name']))
        self.assertTrue(str(element['id']) is not None)
        if test_key is not None and test_value is not None:
            self.assertEqual(str(element['dimensions'][test_key]), test_value)
        if test_name is not None:
            self.assertEqual(str(element['name']), test_name)

    @test.attr(type='gate')
    def test_list_metrics_with_time_args(self):
        name = data_utils.rand_name('name')
        key = data_utils.rand_name('key')
        value_org = data_utils.rand_name('value')

        now = int(round(time.time() * 1000))
        #
        # Built start and end time args before and after the measurement.
        #
        start_iso = helpers.timestamp_to_iso(now - 1000)
        end_timestamp = int(round(now + 1000))
        end_iso = helpers.timestamp_to_iso(end_timestamp)

        metric = helpers.create_metric(name=name,
                                       dimensions={key: value_org},
                                       timestamp=now)

        self.monasca_client.create_metrics(metric)
        for timer in xrange(constants.MAX_RETRIES):
            query_parms = '?name=' + name + '&start_time=' + start_iso + '&end_time=' + end_iso
            resp, response_body = self.monasca_client.list_metrics(query_parms)
            self.assertEqual(200, resp.status)
            elements = response_body['elements']
            if elements:
                dimensions = elements[0]
                dimension = dimensions['dimensions']
                value = dimension[unicode(key)]
                self.assertEqual(value_org, str(value))
                break
            else:
                time.sleep(constants.RETRY_WAIT_SECS)
                if timer == constants.MAX_RETRIES - 1:
                    skip_msg = "Skipped test_list_metrics_with_time_args: " \
                               "timeout on waiting for metrics: at least one " \
                               "metric is needed. Current number of metrics " \
                               "= 0"
                    raise self.skipException(skip_msg)

    @staticmethod
    def _get_metric_dimensions(key_service, values, same_metric_name):
        if same_metric_name:
            metric_dimensions = [{key_service: values[0], 'key3': ''},
                                 {key_service: values[1], 'key3': ''},
                                 {key_service: '', 'key3': 'value3'}]
        else:
            metric_dimensions = [{key_service: values[0]},
                                 {key_service: values[1]},
                                 {'key3': 'value3'}]
        return metric_dimensions

    def _verify_dimensions(self, query_param, metric_dimensions):
        for i in xrange(constants.MAX_RETRIES):
            resp, response_body = self.monasca_client.list_metrics(query_param)
            self.assertEqual(200, resp.status)
            elements = response_body['elements']
            if len(elements) == 2:
                dimension_sets = []
                for element in elements:
                    dimension_sets.append(element['dimensions'])
                self.assertIn(metric_dimensions[0], dimension_sets)
                self.assertIn(metric_dimensions[1], dimension_sets)
                self.assertNotIn(metric_dimensions[2], dimension_sets)
                return
            time.sleep(constants.RETRY_WAIT_SECS)
            if i == constants.MAX_RETRIES - 1:
                error_msg = "Timeout on waiting for metrics: at least " \
                            "2 metrics are needed. Current number of " \
                            "metrics = {}".format(len(elements))
                self.fail(error_msg)

    def _create_metrics_with_different_dimensions(self, same_name=True):
        name1 = data_utils.rand_name('name1')
        name2 = name1 if same_name else data_utils.rand_name('name2')
        name3 = name1 if same_name else data_utils.rand_name('name3')
        key_service = data_utils.rand_name('service')
        values = [data_utils.rand_name('value1'),
                  data_utils.rand_name('value2')]
        metrics = [helpers.create_metric(name1, {key_service: values[0]}),
                   helpers.create_metric(name2, {key_service: values[1]}),
                   helpers.create_metric(name3, {'key3': 'value3'})]
        resp, response_body = self.monasca_client.create_metrics(metrics)
        self.assertEqual(204, resp.status)
        return metrics, name1, key_service, values


text:
/*global Invaders */
/*global Config */
/*global Phaser */
/*global TextButton */
/*jslint plusplus: true */
/*global Menu */

Invaders.Credits = function (game) { 'use strict'; };
Invaders.Credits.prototype = Object.create(Invaders.Base.prototype);

Invaders.Credits.prototype.create = function () {
    'use strict';
    Invaders.Base.prototype.create.call(this);

    this.displayMenu();
    this.displayCreditsTitle();
};

Invaders.Credits.prototype.displayMenu = function () {
    'use strict';
    var x = Config.MAP_WIDTH / 2,
        y = Config.MAP_HEIGHT * 3 / 7 + 20,
        distance = 30;

    this.menu = new Menu(this.game, x, y, distance, this.menus.credits, this.font, 20, { data: "MainMenu"});
};

Invaders.Credits.prototype.displayCreditsTitle = function () {
    'use strict';
    this.creditsTitle = this.game.add.bitmapText(0, 0, this.font,  'Credits', 32);

    this.creditsTitle.updateTransform();
    this.creditsTitle.x = Config.MAP_WIDTH / 2 - this.creditsTitle.width / 2;
    this.creditsTitle.y = Config.MAP_HEIGHT / 3;
    this.creditsTitle.tint = 0xBBAAEE;

};

redpajama_set_name: RedPajamaGithub


text:
#logo {
  max-width: 18%;
	height: auto;
}

.contentonjumbotron {
  text-align: center;
  font-size: 120%;
}

section a {
  margin-left: 5%;
}
section p {
  margin-left: 5%;
  font-size: 1.1em;
}
.care {
  background-color: red;
  color: white;
  font-weight: bold;
  padding: 0.3em;
  border-radius: 10%;
}
code {
  background-color: lightblue;
  border-radius: 10px;
  border-width: 5px;
  padding: 0.1em;
}

/*
header > .clear {
  clear: both;
  height: 0; overflow: hidden; /* Précaution pour IE 7 */

.footer-distributed {
  background-color: #292c2f;
  box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.12);
  box-sizing: border-box;
  width: 100%;
  text-align: left;
  font: normal 16px sans-serif;
  padding: 45px 50px;
  margin-top: 80px;
}
.footer-distributed .footer-left p {
  color: #8f9296;
  font-size: 14px;
  margin: 0;
}

/* Footer links */

.footer-distributed p.footer-links {
  font-size: 18px;
  font-weight: bold;
  color: #ffffff;
  margin: 0 0 10px;
  padding: 0;
}
.footer-distributed p.footer-links a {
  display: inline-block;
  line-height: 1.8;
  text-decoration: none;
  color: inherit;
}
.footer-distributed .footer-right {
  float: right;
  margin-top: 6px;
  max-width: 180px;
}
.footer-distributed .footer-right a {
  display: inline-block;
  width: 35px;
  height: 35px;
  background-color: #33383b;
  border-radius: 2px;
  font-size: 20px;
  color: #ffffff;
  text-align: center;
  line-height: 35px;
  margin-left: 3px;
}

/* If you don't want the footer to be responsive, remove these media queries */

@media (max-width: 600px) {
  #logoroubaix {
    display: none;
  }
  .container-fluid {
    position: relative;
  }
  .firstjumbo {
    margin-top: 15em;
  }
  #menu-toggle {
    margin-top: 2em;
  }
  .imgmargin {
    height: 180px;
    width: 320px;
  }
  .jumbotron {
    margin-left: -1.5em;
  }
  .footer-distributed .footer-left, .footer-distributed .footer-right {
    text-align: center;
  }
  .footer-distributed .footer-right {
    float: none;
    margin: 0 auto 20px;
  }
  .footer-distributed .footer-left p.footer-links {
    line-height: 1.8;
  }
}


text:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using System.Security;
using System.Security.Cryptography;
using System.IO;
using Utility.ModifyRegistry;
using Microsoft.Win32;
using System.Diagnostics;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Threading;
using CSharpAnalytics;
using System.Reflection;

namespace BeanfunLogin
{
    enum LoginMethod : int {
        Regular = 0,
        Keypasco = 1,
        PlaySafe = 2,
        QRCode = 3
    };

    public partial class main : Form
    {
        private AccountManager accountManager = null;

        public BeanfunClient bfClient;

        public BeanfunClient.QRCodeClass qrcodeClass;

        private string service_code = "610074" , service_region = "T9" , service_name = "";

        public List<GameService> gameList = new List<GameService>();

        private CSharpAnalytics.Activities.AutoTimedEventActivity timedActivity = null;

        private Version currentVersion = Assembly.GetExecutingAssembly().GetName().Version;

        private GamePathDB gamePaths = new GamePathDB();

        public main()
        {
            if (Properties.Settings.Default.GAEnabled)
            {
                try
                {
                    AutoMeasurement.Instance = new WinFormAutoMeasurement();
                    AutoMeasurement.DebugWriter = d => Debug.WriteLine(d);
                    AutoMeasurement.Start(new MeasurementConfiguration("UA-75983216-4", Assembly.GetExecutingAssembly().GetName().Name, currentVersion.ToString()));
                }
                catch
                {
                    this.timedActivity = null;
                    Properties.Settings.Default.GAEnabled = false;
                    Properties.Settings.Default.Save();
                }
            }

            this.FormClosing += new FormClosingEventHandler((sender, e) => {
                if (this.bfClient != null) this.bfClient.Logout();
            });

            timedActivity = new CSharpAnalytics.Activities.AutoTimedEventActivity("FormLoad", Properties.Settings.Default.loginMethod.ToString());
            InitializeComponent();
            init();
            CheckForUpdate();

            if (Properties.Settings.Default.GAEnabled && this.timedActivity != null)
            {
                AutoMeasurement.Client.Track(this.timedActivity);
                this.timedActivity = null;
            }
        }

        public void ShowToolTip(IWin32Window ui, string title, string des, int iniDelay = 2000, bool repeat = false)
        {
            if (Properties.Settings.Default.showTip || repeat)
            {
                ToolTip toolTip = new ToolTip();
                toolTip.ToolTipTitle = title;
                toolTip.UseFading = true;
                toolTip.UseAnimation = true;
                toolTip.IsBalloon = true;
                toolTip.InitialDelay = iniDelay;

                toolTip.Show(string.Empty, ui, 3000);
                toolTip.Show(des, ui);
            }
        }

        public bool errexit(string msg, int method, string title = null)
        {
            string originalMsg = msg;
            if (Properties.Settings.Default.GAEnabled)
                AutoMeasurement.Client.TrackException(msg);

            switch (msg)
            {
                case "LoginNoResponse":
                    msg = "初始化失敗,請檢查網路連線。";
                    method = 0;
                    break;
                case "LoginNoSkey":
                    method = 0;
                    break;
                case "LoginNoAkey":
                    msg = "登入失敗,帳號或密碼錯誤。";
                    break;
                case "LoginNoAccountMatch":
                    msg = "登入失敗,無法取得帳號列表。";
                    break;
                case "LoginNoAccount":
                    msg = "找不到遊戲帳號。";
                    break;
                case "LoginNoResponseVakten":
                    msg = "登入失敗,與伺服器驗證失敗,請檢查是否安裝且已執行vakten程式。";
                    break;
                case "LoginUnknown":
                    msg = "登入失敗,請稍後再試";
                    method = 0;
                    break;
                case "OTPNoLongPollingKey":
                    if (Properties.Settings.Default.loginMethod == (int)LoginMethod.PlaySafe)
                        msg = "密碼獲取失敗,請檢查晶片卡是否插入讀卡機,且讀卡機運作正常。\n若仍出現此訊息,請嘗試重新登入。";
                    else
                    {
                        msg = "已從伺服器斷線,請重新登入。";
                        method = 1;
                    }
                    break;
                case "LoginNoReaderName":
                    msg = "登入失敗,找不到晶片卡或讀卡機,請檢查晶片卡是否插入讀卡機,且讀卡機運作正常。\n若還是發生此情形,請嘗試重新登入。";
                    break;
                case "LoginNoCardType":
                    msg = "登入失敗,晶片卡讀取失敗。";
                    break;
                case "LoginNoCardId":
                    msg = "登入失敗,找不到讀卡機。";
                    break;
                case "LoginNoOpInfo":
                    msg = "登入失敗,讀卡機讀取失敗。";
                    break;
                case "LoginNoEncryptedData":
                    msg = "登入失敗,晶片卡讀取失敗。";
                    break;
                case "OTPUnknown":
                    msg = "獲取密碼失敗,請嘗試重新登入。";
                    break;
                case "LoginNoPSDriver":
                    msg = "PlaySafe驅動初始化失敗,請檢查PlaySafe元件是否已正確安裝。";
                    break;
                default:
                    break;
            }

            MessageBox.Show(msg, title);
            if (method == 0)
                Application.Exit();
            else if (method == 1)
            {
                BackToLogin();
            }

            return false;
        }

        public void BackToLogin()
        {
            this.Size = new System.Drawing.Size(459, this.Size.Height);
            panel1.SendToBack();
            panel2.BringToFront();
            Properties.Settings.Default.autoLogin = false;
            init();
            comboBox1_SelectedIndexChanged(null, null);

            for(int i = 0; i < accounts.Items.Count; ++i)
            {
                if ((string)accounts.Items[i] == accountInput.Text)
                {
                    accounts.SelectedIndex = i;
                    break;
                }
            }
        }

        public bool init()
        {
            try
            {
                this.Text = $"BeanfunLogin - v{ currentVersion.Major }.{ currentVersion.Minor }.{ currentVersion.Build }({ currentVersion.Revision })";
                this.AcceptButton = this.loginButton;
                this.bfClient = null;
                this.accountManager = new AccountManager();

                bool res = accountManager.init();
                if (res == false)
                    errexit("帳號記錄初始化失敗,未知的錯誤。", 0);
                refreshAccountList();
                // Properties.Settings.Default.Reset(); //SetToDefault.

                // Handle settings.
                if (Properties.Settings.Default.rememberAccount == true)
                    this.accountInput.Text = Properties.Settings.Default.AccountID;
                if (Properties.Settings.Default.rememberPwd == true)
                {
                    this.rememberAccount.Enabled = false;
                    // Load password.
                    if (File.Exists("UserState.dat"))
                    {
                        try
                        {
                            Byte[] cipher = File.ReadAllBytes("UserState.dat");
                            string entropy = Properties.Settings.Default.entropy;
                            byte[] plaintext = ProtectedData.Unprotect(cipher, Encoding.UTF8.GetBytes(entropy), DataProtectionScope.CurrentUser);
                            this.passwdInput.Text = System.Text.Encoding.UTF8.GetString(plaintext);
                        }
                        catch
                        {
                            File.Delete("UserState.dat");
                        }
                    }
                }
                if (Properties.Settings.Default.autoLogin == true)
                {
                    this.UseWaitCursor = true;
                    this.panel2.Enabled = false;
                    this.loginButton.Text = "請稍後...";
                    this.loginWorker.RunWorkerAsync(Properties.Settings.Default.loginMethod);
                }
                if (gamePaths.Get("新楓之谷") == "")
                {
                    ModifyRegistry myRegistry = new ModifyRegistry();
                    myRegistry.BaseRegistryKey = Registry.CurrentUser;
                    myRegistry.SubKey = "Software\\Gamania\\MapleStory";
                    if (myRegistry.Read("Path") != "")
                    {
                        gamePaths.Set("新楓之谷", myRegistry.Read("Path"));
                        gamePaths.Save();
                    }
                }

                this.loginMethodInput.SelectedIndex = Properties.Settings.Default.loginMethod;
                this.textBox3.Text = "";

                if (this.accountInput.Text == "")
                    this.ActiveControl = this.accountInput;
                else if (this.passwdInput.Text == "")
                    this.ActiveControl = this.passwdInput;

                // .NET textbox full mode bug.
                //this.accountInput.ImeMode = ImeMode.OnHalf;
                //this.passwdInput.ImeMode = ImeMode.OnHalf;
                return true;
            }
            catch (Exception e)
            {
                return errexit("初始化失敗,未知的錯誤。" + e.Message, 0);
            }
        }

        public class GameService
        {
            public string name { get; set; }
            public string service_code { get; set; }
            public string service_region { get; set; }

            public GameService(string name, string service_code, string service_region)
            {
                this.name = name;
                this.service_code = service_code;
                this.service_region = service_region;
            }
        }

        public void CheckForUpdate()
        {
            try
            {
                WebClient wc = new WebClient();
                string res = Encoding.UTF8.GetString(wc.DownloadData("http://tw.beanfun.com/game_zone/"));
                Regex reg = new Regex("Services.ServiceList = (.*);");
                if (reg.IsMatch(res))
                {
                    string json = reg.Match(res).Groups[1].Value;
                    JObject o = JObject.Parse(json);
                    foreach (var game in o["Rows"])
                    {
                        Debug.Write(game["serviceCode"]);
                        this.comboBox2.Items.Add((string)game["ServiceFamilyName"]);
                        gameList.Add(new GameService((string)game["ServiceFamilyName"], (string)game["ServiceCode"], (string)game["ServiceRegion"]));
                    }
                }

                try
                {
                    string gameCode;
                    int gameIndex = 0;
                    foreach (GameService gs in gameList)
                    {
                        gameCode = $"{ gs.service_code }_{ gs.service_region }";
                        if (gameCode.Equals(Properties.Settings.Default.loginGame)) break;
                        gameIndex++;
                    }
                    if (gameList.Count > gameIndex) this.comboBox2.SelectedIndex = gameIndex;
                }
                catch { /* ignore out of range */ }

                const string updateUrl = "https://raw.githubusercontent.com/kevin940726/BeanfunLogin/master/docs/index.md";
                string response = wc.DownloadString(updateUrl);
                Regex regex = new Regex("Version (\\d\\.\\d\\.\\d)");
                if (!regex.IsMatch(response))
                    return;
                string versionStr = regex.Match(response).Groups[1].Value;
                Version webVersion = new Version(versionStr);
                if (versionStr != Properties.Settings.Default.IgnoreVersion && webVersion > currentVersion)
                {
                    Properties.Settings.Default.IgnoreVersion = versionStr;
                    Properties.Settings.Default.Save();

                    Regex versionlog = new Regex(".*此版本更新(.*)### 目錄.*", RegexOptions.Multiline | RegexOptions.Singleline);
                    DialogResult result = MessageBox.Show("有新的版本(" + regex.Match(response).Groups[1].Value + ")可以下載,是否前往下載?\n(此對話窗只會顯示一次)\n\n此版本更新:" + versionlog.Match(response).Groups[1].Value, "檢查更新", MessageBoxButtons.YesNo);

                    if (result == System.Windows.Forms.DialogResult.Yes)
                    {
                        System.Diagnostics.Process.Start("https://kevin940726.github.io/BeanfunLogin");
                    }
                }
            }
            catch { return; }
        }

        private void refreshAccountList()
        {
            string[] accArray = accountManager.getAccountList();
            accounts.Items.Clear();
            accounts.Items.AddRange(accArray);
        }

        // The login botton.
        private void loginButton_Click(object sender, EventArgs e)
        {

            foreach (ListViewItem item in listView1.Items)
                item.BackColor = DefaultBackColor;
            if (this.pingWorker.IsBusy)
            {
                this.pingWorker.CancelAsync();
            }
            if (this.rememberAccount.Checked == true)
                Properties.Settings.Default.AccountID = this.accountInput.Text;
            if (this.rememberAccPwd.Checked == true)
            {
                using (BinaryWriter writer = new BinaryWriter(File.Open("UserState.dat", FileMode.Create)))
                {
                    // Create random entropy of 8 characters.
                    var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
                    var random = new Random();
                    string entropy = new string(Enumerable.Repeat(chars, 8).Select(s => s[random.Next(s.Length)]).ToArray());

                    Properties.Settings.Default.entropy = entropy;
                    writer.Write(ciphertext(this.passwdInput.Text, entropy));
                }
            }
            else
            {
                Properties.Settings.Default.entropy = "";
                File.Delete("UserState.dat");
            }
            Properties.Settings.Default.Save();

            this.UseWaitCursor = true;
            this.panel2.Enabled = false;

            this.loginButton.Text = "請稍後...";
            if (Properties.Settings.Default.GAEnabled)
            {
                timedActivity = new CSharpAnalytics.Activities.AutoTimedEventActivity("Login", Properties.Settings.Default.loginMethod.ToString());
                AutoMeasurement.Client.TrackEvent("Login" + Properties.Settings.Default.loginMethod.ToString(), "Login");
            }
            this.loginWorker.RunWorkerAsync(Properties.Settings.Default.loginMethod);
        }

        // The get OTP button.
        private void getOtpButton_Click(object sender, EventArgs e)
        {
            if (this.pingWorker.IsBusy)
            {
                this.pingWorker.CancelAsync();
            }
            if (listView1.SelectedItems.Count <= 0 || this.loginWorker.IsBusy) return;
            if (Properties.Settings.Default.autoSelect == true)
            {
                Properties.Settings.Default.autoSelectIndex = listView1.SelectedItems[0].Index;
                Properties.Settings.Default.Save();
            }

            this.textBox3.Text = "獲取密碼中...";
            this.listView1.Enabled = false;
            this.getOtpButton.Enabled = false;
            this.comboBox2.Enabled = false;
            if (Properties.Settings.Default.GAEnabled)
            {
                timedActivity = new CSharpAnalytics.Activities.AutoTimedEventActivity("GetOTP", Properties.Settings.Default.loginMethod.ToString());
                AutoMeasurement.Client.TrackEvent("GetOTP" + Properties.Settings.Default.loginMethod.ToString(), "GetOTP");
            }
            this.getOtpWorker.RunWorkerAsync(listView1.SelectedItems[0].Index);
        }

        // Building ciphertext by 3DES.
        private byte[] ciphertext(string plaintext, string key)
        {
            byte[] plainByte = Encoding.UTF8.GetBytes(plaintext);
            byte[] entropy = Encoding.UTF8.GetBytes(key);
            return ProtectedData.Protect(plainByte, entropy, DataProtectionScope.CurrentUser);
        }

        /* Handle other elements' statements. */
        private void BackToLogin_ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            BackToLogin();
        }

        private void SetGamePath_ToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            string identName = comboBox2.SelectedItem.ToString();
            string binaryName = gamePaths.GetAlias(identName);
            if (binaryName == identName) binaryName = "*.exe";
            openFileDialog.Filter = String.Format("{0} ({1})|{1}|All files (*.*)|*.*", identName, binaryName);
            openFileDialog.Title = "Set Path.";
            openFileDialog.InitialDirectory = gamePaths.Get(identName);

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                string file = openFileDialog.FileName;
                gamePaths.Set(identName, file);
                gamePaths.Save();
            }

            if (Properties.Settings.Default.GAEnabled)
            {
                AutoMeasurement.Client.TrackEvent("set game path", "set game path");
            }
        }

        private void checkBox3_CheckedChanged(object sender, EventArgs e)
        {
            if (this.checkBox3.Checked == true)
            {
                Properties.Settings.Default.autoLogin = true;
                this.rememberAccount.Checked = true;
                this.rememberAccPwd.Checked = true;
                this.rememberAccount.Enabled = false;
                this.rememberAccPwd.Enabled = false;
            }
            else
            {
                Properties.Settings.Default.autoLogin = false;
                this.rememberAccount.Enabled = true;
                this.rememberAccPwd.Enabled = true;
            }

            Properties.Settings.Default.Save();

            if (Properties.Settings.Default.GAEnabled)
            {
                AutoMeasurement.Client.TrackEvent(this.checkBox3.Checked ? "autoLoginOn" : "autoLoginOff", "loginCheckbox");
            }
        }

        private void checkBox2_CheckedChanged(object sender, EventArgs e)
        {
            if (this.rememberAccPwd.Checked == true)
            {
                Properties.Settings.Default.rememberPwd = true;
                this.rememberAccount.Checked = true;
                this.rememberAccPwd.Checked = true;
                this.rememberAccount.Enabled = false;
            }
            else
            {
                Properties.Settings.Default.rememberPwd = false;
                this.rememberAccount.Enabled = true;
            }

            if (Properties.Settings.Default.GAEnabled)
            {
                AutoMeasurement.Client.TrackEvent(this.rememberAccPwd.Checked ? "rememberPwdOn" : "rememberPwdOff", "rememberPwdCheckbox");
            }
        }

        private void checkBox4_CheckedChanged(object sender, EventArgs e)
        {
            if (listView1.SelectedItems.Count > 0)
            {
                if (this.checkBox4.Checked == true && this.listView1.SelectedItems[0].Index != -1 && this.listView1.SelectedItems[0].Index <= this.bfClient.accountList.Count())
                {
                    Properties.Settings.Default.autoSelectIndex = this.listView1.SelectedItems[0].Index;
                    Properties.Settings.Default.autoSelect = true;
                }
                else
                    Properties.Settings.Default.autoSelect = false;
            }
            Properties.Settings.Default.Save();

            if (Properties.Settings.Default.GAEnabled)
            {
                AutoMeasurement.Client.TrackEvent(this.checkBox4.Checked ? "autoSelectOn" : "autoSelectOff", "autoSelectCheckbox");
            }
        }

        private void textBox3_OnClick(object sender, EventArgs e)
        {
            if (textBox3.Text == "" || textBox3.Text == "獲取失敗") return;
            try
            {
                Clipboard.SetText(textBox3.Text);
            }
            catch
            {

            }
        }

        private void listView1_DoubleClick(object sender, EventArgs e)
        {
            if (listView1.SelectedItems.Count == 1)
            {
                try
                {
                    Clipboard.SetText(this.bfClient.accountList[this.listView1.SelectedItems[0].Index].sacc);
                }
                catch
                {

                }
            }
        }

        private void listView1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (listView1.SelectedItems.Count == 1)
                this.getOtpButton.Text = "獲取密碼";
        }

        // login method changed event
        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            qrCheckLogin.Enabled = false;

            accountInput.Visible = true;
            accountLabel.Visible = true;

            passLabel.Visible = true;
            passwdInput.Visible = true;

            useNewQRCode.Visible = false;
            qrcodeImg.Visible = false;

            rememberAccount.Visible = true;
            rememberAccPwd.Visible = true;
            checkBox3.Visible = true;
            loginButton.Visible = true;

            wait_qrWorker_notify.Visible = false;

            this.gamaotp_challenge_code_output.Text = "";

            Properties.Settings.Default.loginMethod = this.loginMethodInput.SelectedIndex;

            if (Properties.Settings.Default.loginMethod == (int)LoginMethod.PlaySafe)
            {
                this.passLabel.Text = "PIN碼";
            }
            else if (Properties.Settings.Default.loginMethod == (int)LoginMethod.QRCode)
            {
                accountInput.Visible = false;
                accountLabel.Visible = false;

                passLabel.Visible = false;
                passwdInput.Visible = false;

                useNewQRCode.Visible = true;
                qrcodeImg.Visible = true;

                rememberAccount.Visible = false;
                rememberAccPwd.Visible = false;
                checkBox3.Visible = false;
                loginButton.Visible = false;
                qrcodeImg.Image = null;
                wait_qrWorker_notify.Text = "取得QRCode中 請稍後";
                wait_qrWorker_notify.Visible = true;

                this.qrWorker.RunWorkerAsync(!useNewQRCode.Checked);
                this.loginMethodInput.Enabled = false;
            }
            else
            {
                this.passLabel.Text = "密碼";
            }
        }

        private void keepLogged_CheckedChanged(object sender, EventArgs e)
        {
            if (keepLogged.Checked)
                if (!this.pingWorker.IsBusy)
                    this.pingWorker.RunWorkerAsync();
            else
                    if (this.pingWorker.IsBusy)
                    {
                        this.pingWorker.CancelAsync();
                    }
            Properties.Settings.Default.Save();
        }

        private void delete_Click(object sender, EventArgs e)
        {
            ListBox.SelectedObjectCollection selectedItems = new ListBox.SelectedObjectCollection(accounts);
            selectedItems = accounts.SelectedItems;

            if (accounts.SelectedIndex != -1)
            {
                for (int i = selectedItems.Count - 1; i >= 0; i--)
                {
                    accountManager.removeAccount(accounts.GetItemText(selectedItems[i]));
                    refreshAccountList();
                }
            }

            if (Properties.Settings.Default.GAEnabled)
            {
                AutoMeasurement.Client.TrackEvent("remove", "accountMananger");
            }
        }

        private void import_Click(object sender, EventArgs e)
        {
            bool res = accountManager.addAccount(accountInput.Text, passwdInput.Text, loginMethodInput.SelectedIndex);
            if (res == false)
                errexit("帳號記錄新增失敗",0);
            refreshAccountList();

            if (Properties.Settings.Default.GAEnabled)
            {
                AutoMeasurement.Client.TrackEvent("add", "accountMananger");
            }
        }

        private void export_Click(object sender, EventArgs e)
        {
            if(accounts.SelectedIndex != -1)
            {
                string account = accounts.SelectedItem.ToString();
                string passwd = accountManager.getPasswordByAccount(account);
                int method = accountManager.getMethodByAccount(account);

                if( passwd == null || method == -1 )
                {
                    errexit("帳號記錄讀取失敗。", 0);
                }

                accountInput.Text = account;
                passwdInput.Text = passwd;
                loginMethodInput.SelectedIndex = method;

                if (Properties.Settings.Default.GAEnabled)
                {
                    AutoMeasurement.Client.TrackEvent("fill", "accountMananger");
                }
            }
        }

        private void autoPaste_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.Save();

            if (Properties.Settings.Default.GAEnabled)
            {
                AutoMeasurement.Client.TrackEvent(this.autoPaste.Checked ? "autoPasteOn" : "autoPasteOff", "autoPasteCheckbox");
            }
        }

        private void rememberAccount_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.Save();

            if (Properties.Settings.Default.GAEnabled)
            {
                AutoMeasurement.Client.TrackEvent(this.rememberAccount.Checked ? "rememberAccountOn" : "rememberAccountOff", "rememberAccountCheckbox");
            }
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.Save();

            if (Properties.Settings.Default.GAEnabled)
            {
                AutoMeasurement.Client.TrackEvent(this.checkBox1.Checked ? "autoLaunchOn" : "autoLaunchOff", "autoLaunchCheckbox");
            }
        }

        private void label1_Click(object sender, EventArgs e)
        {
            var f = new FormAccRecovery(this.accountManager);
            f.ShowDialog();
            refreshAccountList();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.bfClient.Logout();
            BackToLogin();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            string identName = comboBox2.SelectedItem.ToString();
            string binaryName = gamePaths.GetAlias(identName);
            if (binaryName == identName) binaryName = "*.exe";
            openFileDialog.Filter = String.Format("{0} ({1})|{1}|All files (*.*)|*.*", identName, binaryName);
            openFileDialog.Title = "Set Path.";
            openFileDialog.InitialDirectory = gamePaths.Get(identName);

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                string file = openFileDialog.FileName;
                gamePaths.Set(identName, file);
                gamePaths.Save();
            }

            if (Properties.Settings.Default.GAEnabled)
            {
                AutoMeasurement.Client.TrackEvent("set game path", "set game path");
            }
        }

        private void useNewQRCode_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.Save();
            if (this.loginMethodInput.SelectedIndex == (int)LoginMethod.QRCode)
            {
                this.qrWorker.RunWorkerAsync(!useNewQRCode.Checked);
            }
        }

        // game changed event
        private void comboBox2_SelectedIndexChanged_1(object sender, EventArgs e)
        {
            GameService selectedGS = gameList[this.comboBox2.SelectedIndex];
            string gameCode = $"{ selectedGS.service_code }_{ selectedGS.service_region }";
            Properties.Settings.Default.loginGame = gameCode;
            try
            {
                service_code = gameList[this.comboBox2.SelectedIndex].service_code;
                service_region = gameList[this.comboBox2.SelectedIndex].service_region;
                service_name = comboBox2.SelectedItem.ToString();
            }
            catch
            {
                return;
            }

            if (this.bfClient != null && !loginWorker.IsBusy && !getOtpWorker.IsBusy)
            {
                comboBox2.Enabled = false;
                this.bfClient.GetAccounts(service_code, service_region);
                redrawSAccountList();
                comboBox2.Enabled = true;
                if (this.bfClient.errmsg != null)
                {
                    errexit(this.bfClient.errmsg, 2);
                    this.bfClient.errmsg = null;
                }
            }
        }

        private void main_FormClosed(object sender, FormClosedEventArgs e)
        {
            gamePaths.Save();
            Properties.Settings.Default.Save();
        }
    }
}


text:
#ifndef CHARLS_LOSSLESSTRAITS
#define CHARLS_LOSSLESSTRAITS

#include "defaulttraits.h"

//
// optimized trait classes for lossless compression of 8 bit color and 8/16 bit monochrome images.
// This class is assumes MAXVAL correspond to a whole number of bits, and no custom RESET value is set when encoding.
// The point of this is to have the most optimized code for the most common and most demanding scenario.

template<typename sample, int32_t bitsperpixel>
struct LosslessTraitsImplT
{
    typedef sample SAMPLE;
    enum
    {
        NEAR  = 0,
        bpp   = bitsperpixel,
        qbpp  = bitsperpixel,
        RANGE = (1 << bpp),
        MAXVAL= (1 << bpp) - 1,
        LIMIT = 2 * (bitsperpixel + MAX(8,bitsperpixel)),
        RESET = BASIC_RESET
    };

    static inlinehint int32_t ComputeErrVal(int32_t d)
    {
        return ModRange(d);
    }

    static inlinehint bool IsNear(int32_t lhs, int32_t rhs)
    {
        return lhs == rhs;
    }

    static inlinehint int32_t ModRange(int32_t Errval)
    {
        return int32_t(Errval << (INT32_BITCOUNT  - bpp)) >> (INT32_BITCOUNT  - bpp);
    }

    static inlinehint SAMPLE ComputeReconstructedSample(int32_t Px, int32_t ErrVal)
    {
        return SAMPLE(MAXVAL & (Px + ErrVal));
    }

    static inlinehint int32_t CorrectPrediction(int32_t Pxc)
    {
        if ((Pxc & MAXVAL) == Pxc)
            return Pxc;

        return (~(Pxc >> (INT32_BITCOUNT-1))) & MAXVAL;
    }
};

template<typename SAMPLE, int32_t bpp>
struct LosslessTraitsT : LosslessTraitsImplT<SAMPLE, bpp>
{
    typedef SAMPLE PIXEL;
};

template<>
struct LosslessTraitsT<uint8_t, 8> : LosslessTraitsImplT<uint8_t, 8>
{
    typedef SAMPLE PIXEL;

    static inlinehint signed char ModRange(int32_t Errval)
    {
        return static_cast<signed char>(Errval);
    }

    static inlinehint int32_t ComputeErrVal(int32_t d)
    {
        return static_cast<signed char>(d);
    }

    static inlinehint uint8_t ComputeReconstructedSample(int32_t Px, int32_t ErrVal)
    {
        return uint8_t(Px + ErrVal);
    }
};

template<>
struct LosslessTraitsT<uint16_t, 16> : LosslessTraitsImplT<uint16_t, 16>
{
    typedef SAMPLE PIXEL;

    static inlinehint short ModRange(int32_t Errval)
    {
        return short(Errval);
    }

    static inlinehint int32_t ComputeErrVal(int32_t d)
    {
        return short(d);
    }

    static inlinehint SAMPLE ComputeReconstructedSample(int32_t Px, int32_t ErrVal)
    {
        return SAMPLE(Px + ErrVal);
    }
};

template<typename SAMPLE, int32_t bpp>
struct LosslessTraitsT<Triplet<SAMPLE>, bpp> : LosslessTraitsImplT<SAMPLE, bpp>
{
    typedef Triplet<SAMPLE> PIXEL;

    static inlinehint bool IsNear(int32_t lhs, int32_t rhs)
    {
        return lhs == rhs;
    }

    static inlinehint bool IsNear(PIXEL lhs, PIXEL rhs)
    {
        return lhs == rhs;
    }

    static inlinehint SAMPLE ComputeReconstructedSample(int32_t Px, int32_t ErrVal)
    {
        return SAMPLE(Px + ErrVal);
    }
};

#endif


text:
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package mms.Pluginsystem;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Stream;
import javafx.beans.value.ChangeListener;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.control.Menu;
import javafx.scene.layout.Pane;
import javafx.scene.media.MediaErrorEvent;
import javafx.scene.media.MediaException;
import javafx.scene.media.MediaPlayer;

/**
 * This is the API for the plugin - devs, here are all methods listed which are
 * reachable for them.
 *
 * @author Michael Obermüller
 */
public abstract class PluginHost {

    protected final List<Plugin> loadedPlugins = new ArrayList<>();
    protected final Set<Plugin> playerListener = new CopyOnWriteArraySet<>();

    /**
     * Call this method if you want to know all supported Audio formats
     * @return all supported formats
     */
    public final static String[] getSupportedAudioFormats(){
        return new String[]{"*.mp3", "*.aif", "*.aiff", "*.wav", "*.m4a"};
    }

    /**
     * Call this method if you want to know all supported Video formats
     * @return all supported formats
     */
    public final static String[] getSupportedVideoFormats(){
        return new String[]{"*.fxm", "*.flv", "*.mp4", "*.m4v"};
    }

    /**
     * Registers your plugin as a listener on arbitrary other plugins
     *
     * You have to override the following method:
     *
     * @see Plugin#onEventReceived(java.lang.String, java.lang.Object...)
     *
     * @param listener instance of your plugin
     * @param id use static Identifier class!
     * @return true if expected plugin exists
     */
    public final boolean registerPluginListener(Plugin listener, Identifier id) {
        Plugin plugin;
        Object ident = id.obj;

        try {
            if (ident.equals(ControlPlugin.class)) {
                plugin = loadedPlugins.stream().filter(p -> p instanceof ControlPlugin).findFirst().get();
            } else if (ident.equals(MenuPlugin.class)) {
                plugin = loadedPlugins.stream().filter(p -> p instanceof MenuPlugin).findFirst().get();
            } else { //Must be an other plugin
                if (ident instanceof String) {
                    plugin = loadedPlugins.stream().filter(p -> p.getID() == ident.hashCode()).findFirst().get();
                } else { //must be Integer
                    plugin = loadedPlugins.stream().filter(p -> p.getID() == (int) ident).findFirst().get();
                }
            }
            plugin.addListener(listener);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * Deregisters your plugin
     *
     * @param listener instance of your plugin
     * @param id use static Identifier class!
     * @return true if successful
     */
    public final boolean deregisterPluginListener(Plugin listener, Identifier id) {
        Stream<Plugin> plugins;
        Object ident = id.obj;

        if (ident.equals(ControlPlugin.class)) {
            plugins = loadedPlugins.stream().filter(p -> p instanceof ControlPlugin);
        } else if (ident.equals(MenuPlugin.class)) {
            plugins = loadedPlugins.stream().filter(p -> p instanceof MenuPlugin);
        } else { //Must be an other plugin
            if (ident instanceof String) {
                plugins = loadedPlugins.stream().filter(p -> p.getID() == ident.hashCode());
            } else { //must be Integer
                plugins = loadedPlugins.stream().filter(p -> p.getID() == (int) ident);
            }
        }

        Plugin[] result = plugins.toArray((int value) -> new Plugin[value]);

        return result.length != 1 ? false : result[0].removeListener(listener);
    }

    /**
     * Sends message to a plugin with the specified identifier
     *
     * @param id use static Identifier class!
     * @param msgID ID of message (other plugins should know that)
     * @param args arbitrary objects (other plugins should know the type)
     * @return true if other plugin found and successfully sended your message
     */
    public final boolean fireMessageDirectlyToPlugin(Identifier id, String msgID, Object... args) {
        Stream<Plugin> plugins;
        Object ident = id.obj;

        if (ident.equals(ControlPlugin.class)) {
            plugins = loadedPlugins.stream().filter(p -> p instanceof ControlPlugin);
        } else if (ident.equals(MenuPlugin.class)) {
            plugins = loadedPlugins.stream().filter(p -> p instanceof MenuPlugin);
        } else { //Must be an other plugin
            if (ident instanceof String) {
                plugins = loadedPlugins.stream().filter(p -> p.getID() == ident.hashCode());
            } else { //must be Integer
                plugins = loadedPlugins.stream().filter(p -> p.getID() == (int) ident);
            }
        }

        Plugin[] result = plugins.toArray((int value) -> new Plugin[value]);

        if (result.length == 1) {
            result[0].onEventReceived(msgID, args);
            return true;
        }
        return false;
    }

    /**
     * Adds a javaFX pane to the GUIstack (last added pane is on top)
     *
     * @param pane to add
     */
    public abstract void addToUIStack(Pane pane);

    /**
     * With this method it is possible to play new media-files
     *
     * @param player with new mediafile
     * @deprecated please use pluginHost.setMedia(URI src) instead!
     */
    public abstract void setPlayer(MediaPlayer player);

    /**
     * With this method it is possible to play new Media-Files, the user will be
     * informed by an AltertMessage if an error occurs. Throws the
     * MediaException again, you may catch it to do further errorhandling...
     *
     * @param src the URI to the new media-Source
     * @throws MediaException (RuntimeException)
     */
    public abstract void setMedia(URI src) throws MediaException;

    /**
     * Registers an event handler to mainUI - node. The handler is called when
     * the node receives an Event of the specified type during the bubbling
     * phase of event delivery.
     *
     * @param <T> the specific event class of the handler
     * @param eventType the type of the events to receive by the handler
     * @param eventHandler the handler to register
     */
    public abstract <T extends Event> void addUIEventHandler(final EventType<T> eventType, final EventHandler<? super T> eventHandler);

    /**
     * Registers an event filter to mainUI - node. The filter is called when the
     * node receives an Event of the specified type during the capturing phase
     * of event delivery.
     *
     * @param <T> the specific event class of the filter
     * @param eventType the type of the events to receive by the filter
     * @param eventFilter the filter to register
     */
    public abstract <T extends Event> void addUIEventFilter(final EventType<T> eventType, final EventHandler<? super T> eventFilter);

    /**
     * Unregisters a previously registered event handler from mainUI - node. One
     * handler might have been registered for different event types, so the
     * caller needs to specify the particular event type from which to
     * unregister the handler.
     *
     * @param <T> the specific event class of the handler
     * @param eventType the event type from which to unregister
     * @param eventHandler the handler to unregister
     */
    public abstract <T extends Event> void removeUIEventHandler(final EventType<T> eventType, final EventHandler<? super T> eventHandler);

    /**
     * The menus to show within this MenuBar. If this ObservableList is modified
     * at runtime, the MenuBar will update as expected
     *
     * @return the list of menus
     */
    public abstract ObservableList<Menu> getMenus();

    /**
     * If you want to get informed when the mediaplayer starts to play a new
     * media you have to register here!
     *
     * You have to override the following "see also" method:
     *
     * @see Plugin#onMediaPlayerChanged(javafx.scene.media.MediaPlayer)
     *
     * @param plugin your plugin instance
     */
    public final void addMediaListener(Plugin plugin) {
        playerListener.add(plugin);
    }

    /**
     * Deregisters your plugin from the mediaListeners
     *
     * @param plugin your plugin instance
     * @return true if successful
     */
    public final boolean removeMediaListener(Plugin plugin) {
        return playerListener.remove(plugin);
    }

    /**
     * Unregisters a previously registered event filter from mainUI - node. One
     * filter might have been registered for different event types, so the
     * caller needs to specify the particular event type from which to
     * unregister the filter.
     *
     * @param <T> the specific event class of the filter
     * @param eventType the event type from which to unregister
     * @param eventFilter the filter to unregister
     */
    public abstract <T extends Event> void removeUIEventFilter(final EventType<T> eventType, final EventHandler<? super T> eventFilter);

    /**
     * If you want to listen on Errors of reading Media... (Errors from the
     * Mediaview)
     *
     * @param handler your Errorhandler
     */
    public abstract void addMediaErrorHandler(ChangeListener<EventHandler<MediaErrorEvent>> handler);

    /**
     * Unregisters a previously registered EventListener
     *
     * @param handler your Errorhandler
     */
    public abstract void removeMediaErrorHandler(ChangeListener<EventHandler<MediaErrorEvent>> handler);

    public final static class Identifier {

        private final Object obj;

        private Identifier(Object o) {
            this.obj = o;
        }

        public static Identifier ControlPlugin() {
            return new Identifier(ControlPlugin.class);
        }

        public static Identifier MenuPlugin() {
            return new Identifier(MenuPlugin.class);
        }

        /**
         * You need to know the Developers name, plugin name and the version
         *
         * @param identifier example: "DevName,PluginName,Version"
         * @return
         */
        public static Identifier Plugin(String identifier) {
            return new Identifier(identifier);
        }
    }
}


text:
namespace gl {

class EGLApiTest : public testing::Test {
 public:
  void SetUp() override {
    fake_client_extension_string_ = "";
    fake_extension_string_ = "";

    g_driver_egl.fn.eglInitializeFn = &FakeInitialize;
    g_driver_egl.fn.eglTerminateFn = &FakeTerminate;
    g_driver_egl.fn.eglQueryStringFn = &FakeQueryString;
    g_driver_egl.fn.eglGetCurrentDisplayFn = &FakeGetCurrentDisplay;
    g_driver_egl.fn.eglGetDisplayFn = &FakeGetDisplay;
    g_driver_egl.fn.eglGetErrorFn = &FakeGetError;
    g_driver_egl.fn.eglGetProcAddressFn = &FakeGetProcAddress;

#if defined(OS_WIN)
    SetGLImplementation(kGLImplementationEGLANGLE);
#else
    SetGLImplementation(kGLImplementationEGLGLES2);
#endif
  }

  void TearDown() override {
    init::ShutdownGL(false);
    api_.reset(nullptr);

    fake_client_extension_string_ = "";
    fake_extension_string_ = "";
  }

  void InitializeAPI(const char* disabled_extensions) {
    api_ = std::make_unique<RealEGLApi>();
    g_current_egl_context = api_.get();
    api_->Initialize(&g_driver_egl);
    if (disabled_extensions) {
      SetDisabledExtensionsEGL(disabled_extensions);
    }
    g_driver_egl.InitializeClientExtensionBindings();
    GLSurfaceEGL::InitializeDisplay(EGLDisplayPlatform(EGL_DEFAULT_DISPLAY));
    g_driver_egl.InitializeExtensionBindings();
  }

  void SetFakeExtensionString(const char* fake_string,
                              const char* fake_client_string) {
    fake_extension_string_ = fake_string;
    fake_client_extension_string_ = fake_client_string;
  }

  static EGLBoolean GL_BINDING_CALL FakeInitialize(EGLDisplay display,
                                                   EGLint * major,
                                                   EGLint * minor) {
    return EGL_TRUE;
  }

  static EGLBoolean GL_BINDING_CALL FakeTerminate(EGLDisplay dpy) {
    return EGL_TRUE;
  }

  static const char* GL_BINDING_CALL FakeQueryString(EGLDisplay dpy,
                                                     EGLint name) {
    if (dpy == EGL_NO_DISPLAY) {
      return fake_client_extension_string_;
    } else {
      return fake_extension_string_;
    }
  }

  static EGLDisplay GL_BINDING_CALL FakeGetCurrentDisplay() {
    return reinterpret_cast<EGLDisplay>(0x1);
  }

  static EGLDisplay GL_BINDING_CALL FakeGetDisplay(
      EGLNativeDisplayType native_display) {
    return reinterpret_cast<EGLDisplay>(0x1);
  }

  static EGLint GL_BINDING_CALL FakeGetError() {
    return EGL_SUCCESS;
  }

  static __eglMustCastToProperFunctionPointerType GL_BINDING_CALL
  FakeGetProcAddress(const char* procname) {
    return nullptr;
  }

  std::pair<const char*, const char*> GetExtensions() {
    return std::make_pair(
        api_->eglQueryStringFn(EGL_NO_DISPLAY, EGL_EXTENSIONS),
        api_->eglQueryStringFn(api_->eglGetCurrentDisplayFn(), EGL_EXTENSIONS));
  }

 protected:
  static const char* fake_extension_string_;
  static const char* fake_client_extension_string_;

  std::unique_ptr<RealEGLApi> api_;
};

const char* EGLApiTest::fake_extension_string_ = "";
const char* EGLApiTest::fake_client_extension_string_ = "";

TEST_F(EGLApiTest, DisabledExtensionBitTest) {
  static const char* kFakeExtensions = "EGL_KHR_fence_sync";
  static const char* kFakeClientExtensions = "";
  static const char* kFakeDisabledExtensions = "EGL_KHR_fence_sync";

  SetFakeExtensionString(kFakeExtensions, kFakeClientExtensions);
  InitializeAPI(nullptr);

  EXPECT_TRUE(g_driver_egl.ext.b_EGL_KHR_fence_sync);

  InitializeAPI(kFakeDisabledExtensions);

  EXPECT_FALSE(g_driver_egl.ext.b_EGL_KHR_fence_sync);
}

TEST_F(EGLApiTest, DisabledExtensionStringTest) {
  static const char* kFakeExtensions =
      "EGL_EXT_1 EGL_KHR_fence_sync EGL_EXT_3 EGL_KHR_wait_sync";
  static const char* kFakeClientExtensions =
      "EGL_CLIENT_EXT_1 EGL_CLIENT_EXT_2";
  static const char* kFakeDisabledExtensions =
      "EGL_KHR_fence_sync,EGL_KHR_wait_sync";
  static const char* kFilteredExtensions = "EGL_EXT_1 EGL_EXT_3";
  static const char* kFilteredClientExtensions =
      "EGL_CLIENT_EXT_1 EGL_CLIENT_EXT_2";

  SetFakeExtensionString(kFakeExtensions, kFakeClientExtensions);
  InitializeAPI(nullptr);

  EXPECT_STREQ(kFakeClientExtensions, GetExtensions().first);
  EXPECT_STREQ(kFakeExtensions, GetExtensions().second);

  InitializeAPI(kFakeDisabledExtensions);

  EXPECT_STREQ(kFilteredClientExtensions, GetExtensions().first);
  EXPECT_STREQ(kFilteredExtensions, GetExtensions().second);
}

}  // namespace gl


text:
/*jshint node:true, mocha:true*/

'use strict';
describe('Export', function () {
    var testFixture = require('../../../globals'),
        path = testFixture.path,
        assert = require('assert'),
        SEED_DIR = path.join(testFixture.DF_SEED_DIR, 'devProject'),
        gmeConfig = testFixture.getGmeConfig(),
        expect = testFixture.expect,
        logger = testFixture.logger.fork('Export'),
        PluginCliManager = testFixture.WebGME.PluginCliManager,
        manager = new PluginCliManager(null, logger, gmeConfig),
        BlobClient = require('webgme-engine/src/server/middleware/blob/BlobClientWithFSBackend'),
        projectName = 'testProject',
        pluginName = 'Export',
        project,
        gmeAuth,
        storage,
        commitHash;

    before(function (done) {
        testFixture.clearDBAndGetGMEAuth(gmeConfig, projectName)
            .then(function (gmeAuth_) {
                gmeAuth = gmeAuth_;
                // This uses in memory storage. Use testFixture.getMongoStorage to persist test to database.
                storage = testFixture.getMemoryStorage(logger, gmeConfig, gmeAuth);
                return storage.openDatabase();
            })
            .then(function () {
                var importParam = {
                    projectSeed: path.join(SEED_DIR, 'devProject.webgmex'),
                    projectName: projectName,
                    branchName: 'master',
                    logger: logger,
                    gmeConfig: gmeConfig
                };

                return testFixture.importProject(storage, importParam);
            })
            .then(function (importResult) {
                project = importResult.project;
                commitHash = importResult.commitHash;
                return project.createBranch('test', commitHash);
            })
            .nodeify(done);
    });

    after(function (done) {
        storage.closeDatabase()
            .then(function () {
                return gmeAuth.unload();
            })
            .nodeify(done);
    });

    it('should run plugin and NOT update the branch', function (done) {
        var pluginConfig = {},
            context = {
                namespace: 'pipeline',
                project: project,
                commitHash: commitHash,
                branchName: 'test',
                activeNode: '/f/s'
            };

        manager.executePlugin(pluginName, pluginConfig, context, function (err, pluginResult) {
            try {
                expect(err).to.equal(null);
                expect(typeof pluginResult).to.equal('object');
                expect(pluginResult.success).to.equal(true);

                project.getBranchHash('test')
                    .then(function (branchHash) {
                        expect(branchHash).to.equal(commitHash);
                    })
                    .nodeify(done);
            } catch (err) {
                done(err);
            }
        });
    });

    // TODO: Add test cases
});


text:
<?php
/**
 *
 * Description
 *
 * @package        100 Days of Code
 * @category       Source
 * @author         Michael Akanji <matscode@gmail.com> {@link http://michaelakanji.com}
 * @date           2017-10-07
 *
 */

// validate number
function numberMatch( $userNumber, $randNumber )
{
	if ( ! is_numeric( $userNumber ) ) return 'Only numbers are allowed';

	if ( $userNumber < $randNumber )return 'Your guess is lower';

	if ( $userNumber > $randNumber ) return 'Your guess is higher';

	return ( $userNumber == $randNumber );
}

function resetGame( )
{
	session_destroy();
	// new game
	header( 'location: ' . $_SERVER["PHP_SELF"] );
}

// persistence
session_start();

// reset game
if (@$_GET['resetGame']) resetGame(); // forReal?

// get user input
$userNumber = @$_GET['number'];

// generate random number
$randNumber = $_SESSION['randNumber']
	= isset( $_SESSION['randNumber'] )
	? $_SESSION['randNumber']
	: rand( 1, 10 );

// validate input (applaude || request input)
echo ( ( $message = numberMatch( $userNumber, $randNumber ) ) !== true )
	? '<form><input name="number" placeholder="Number"><input type="submit" value="Guess"></form>
' . $message . '
'
	: '
Now go home, you guess the number ryt :-D... New Game
';