/**
 * Copyright 2013 Netflix, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.netflix.servo.monitor;

import com.netflix.servo.DefaultMonitorRegistry;
import com.netflix.servo.tag.BasicTagList;
import com.netflix.servo.tag.TagList;
import org.testng.annotations.Test;

import java.util.List;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;

public class TimedInterfaceTest  {
    /**
     * Dummy interface to test our timer
     */
    private static interface IDummy {
        void method1();
        boolean method2(int n);
        Object method3(Object a, Object b);
    }

    private static class DummyImpl implements IDummy {
        private void sleep(int ms) {
            try {
                Thread.sleep(ms);
            } catch (InterruptedException ignored) {
            }
        }

        @Override
        public void method1() {
            sleep(5);
        }

        @Override
        public boolean method2(int n) {
            sleep(15);
            return n > 0;
        }

        @Override
        public Object method3(Object a, Object b) {
            sleep(30);
            return a;
        }
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testTimedInterface() {
        final IDummy dummy = TimedInterface.newProxy(IDummy.class, new DummyImpl(), "id");

        // you'd register the CompositeMonitor as:
        DefaultMonitorRegistry.getInstance().register((CompositeMonitor)dummy);

        for (int i = 0; i < 42; i++) {
            dummy.method1();
            if (i % 2 == 0) {
                dummy.method2(i);
            }
        }

        final CompositeMonitor<Long> compositeMonitor = (CompositeMonitor<Long>) dummy;
        final List<Monitor<?>> monitors = compositeMonitor.getMonitors();
        assertEquals(monitors.size(), 2);
        assertEquals(compositeMonitor.getValue().longValue(), 2L);

        final TagList tagList = BasicTagList.of(
                TimedInterface.CLASS_TAG, "DummyImpl",
                TimedInterface.INTERFACE_TAG, "IDummy",
                TimedInterface.ID_TAG, "id");
        final MonitorConfig expectedConfig = MonitorConfig.builder(TimedInterface.TIMED_INTERFACE)
                .withTags(tagList).build();

        assertEquals(compositeMonitor.getConfig(), expectedConfig);

        for (Monitor<?> monitor: monitors) {
            final MonitorConfig config = monitor.getConfig();
            final String method = config.getName();
            final MonitorConfig expected = MonitorConfig.builder(method).withTags(tagList).build();
            assertEquals(config, expected);
            if (method.equals("method1")) {
                // expected result is 5, but let's give it a fudge factor to account for slow machines
                long value = ((Monitor<Long>) monitor).getValue() - 5;
                assertTrue(value >= 0 && value <= 4);
            } else {
                assertEquals(method, "method2");
                // expected result is 15, but let's give it a fudge factor to account for slow machines
                long value = ((Monitor<Long>) monitor).getValue() - 15;
                assertTrue(value >= 0 && value <= 4);
            }
        }
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testTimedInterfaceNoId() {
        final IDummy dummy = TimedInterface.newProxy(IDummy.class, new DummyImpl());

        // you'd register the CompositeMonitor as:
        DefaultMonitorRegistry.getInstance().register((CompositeMonitor)dummy);

        final CompositeMonitor<Long> compositeMonitor = (CompositeMonitor<Long>) dummy;
        final TagList tagList = BasicTagList.of(
                TimedInterface.CLASS_TAG, "DummyImpl",
                TimedInterface.INTERFACE_TAG, "IDummy");
        final MonitorConfig expectedConfig = MonitorConfig.builder(TimedInterface.TIMED_INTERFACE)
                .withTags(tagList).build();
        assertEquals(compositeMonitor.getConfig(), expectedConfig);
    }
}

@@ -1436,17 +1436,18 @@
   sleep(
-5
+20
 );%0A     
@@ -1528,18 +1528,18 @@
   sleep(
-15
+40
 );%0A     
@@ -1658,17 +1658,17 @@
   sleep(
-3
+6
 0);%0A    
@@ -3243,17 +3243,18 @@
 sult is 
-5
+20
 , but le
@@ -3377,17 +3377,18 @@
 lue() - 
-5
+20
 ;%0A      
@@ -3423,33 +3423,33 @@
 = 0 && value %3C= 
-4
+9
 );%0A            %7D
@@ -3543,18 +3543,18 @@
 sult is 
-15
+40
 , but le
@@ -3681,10 +3681,10 @@
 ) - 
-15
+40
 ;%0A  
@@ -3731,17 +3731,17 @@
 alue %3C= 
-4
+9
 );%0A     

Increase sleep time and use multiples of ten. Allow up to 9ms off in the avg.