/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.trace;

import java.util.List;
import static net.ranides.assira.junit.NewAssert.*;
import net.ranides.assira.text.Wildcard;
import org.junit.Test;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class TraceUtilsTest {

    @Test
    public void testCallerType() {
        assertTrue( SI0.runCallerType0() );
    }
    
    @Test
    public void testCalleeType() {
        assertTrue( SI0.runCalleeType0() );
    }
    
    @Test
    public void testCallerName() {
        assertTrue( SI0.runCallerName0() );
    }
    
    @Test
    public void testCalleeName() {
        assertTrue( SI0.runCalleeName0() );
    }
    
    @Test
    public void testGetTypes0() {
        assertTrue( SI0.runGetTypes0() );
    }
    
    @Test
    public void testGetNames() {
        assertTrue( SI0.runGetNames0() );
    }
    
    @Test
    public void testFrame() {
        assertTrue( SI0.runFrame0() );
    }
    
    private static final class SI0 {
        
        private SI0() { /* utility class */ }
        
        public static boolean runCallerType0() {
            SI1.runCallerType1();
            return true;
        }
        
        public static boolean runCalleeType0() {
            SI1.runCalleeType1();
            return true;
        }
        
        public static boolean runCallerName0() {
            SI1.runCallerName1();
            return true;
        }
        
        public static boolean runCalleeName0() {
            SI1.runCalleeName1();
            return true;
        }
        
        public static boolean runGetTypes0() {
            SI1.runGetTypes1();
            return true;
        }
        
        public static boolean runGetNames0() {
            SI1.runGetNames1();
            return true;
        }
        
        public static boolean runFrame0() {
            SI1.runFrame1();
            return true;
        }
        
    }
    
    private static final class SI1 {
        
        private SI1() { /* utility class */ }
        
        public static void runCallerType1() {
            Class<?> caller = SI0.class;

            Class callerAuto = TraceUtils.getCallerType();
            Class callerSM   = TraceUtils.test_getCallerType(TraceUtils.RESOLVER_SM);
            Class callerSR   = TraceUtils.test_getCallerType(TraceUtils.RESOLVER_SR);

            assertEquals(caller, callerAuto);
            assertEquals(caller, callerSM);
            assertEquals(caller, callerSR);
            
            assertThrows(UnsupportedOperationException.class, ()->{
                TraceUtils.test_getCallerType(TraceUtils.RESOLVER_TS);
            });
            assertThrows(UnsupportedOperationException.class, ()->{
                TraceUtils.test_getCallerType(TraceUtils.RESOLVER_ES);
            });
        }
        
        public static void runCalleeType1() {
            Class<?> callee = SI1.class;

            Class calleeAuto = TraceUtils.getCalleeType();
            Class calleeSM   = TraceUtils.test_getCalleeType(TraceUtils.RESOLVER_SM);
            Class calleeSR   = TraceUtils.test_getCalleeType(TraceUtils.RESOLVER_SR);

            assertEquals(callee, calleeAuto);
            assertEquals(callee, calleeSM);
            assertEquals(callee, calleeSR);
            
            assertThrows(UnsupportedOperationException.class, ()->{
                TraceUtils.test_getCalleeType(TraceUtils.RESOLVER_TS);
            });
            assertThrows(UnsupportedOperationException.class, ()->{
                TraceUtils.test_getCalleeType(TraceUtils.RESOLVER_ES);
            });
        }
        
        public static void runCallerName1() {
            String caller = SI0.class.getName();
            
            String callerAuto = TraceUtils.getCallerName();
            String callerSM   = TraceUtils.test_getCallerName(TraceUtils.RESOLVER_SM);
            String callerSR   = TraceUtils.test_getCallerName(TraceUtils.RESOLVER_SR);
            String callerTS   = TraceUtils.test_getCallerName(TraceUtils.RESOLVER_TS);
            String callerES   = TraceUtils.test_getCallerName(TraceUtils.RESOLVER_ES);

            assertEquals(caller, callerAuto);
            assertEquals(caller, callerSM);
            assertEquals(caller, callerSR);
            assertEquals(caller, callerTS);
            assertEquals(caller, callerES);
        }
        
        public static void runCalleeName1() {
            String callee = SI1.class.getName();
            
            String calleeAuto = TraceUtils.getCalleeName();
            String calleeSM   = TraceUtils.test_getCalleeName(TraceUtils.RESOLVER_SM);
            String calleeSR   = TraceUtils.test_getCalleeName(TraceUtils.RESOLVER_SR);
            String calleeTS   = TraceUtils.test_getCalleeName(TraceUtils.RESOLVER_TS);
            String calleeES   = TraceUtils.test_getCalleeName(TraceUtils.RESOLVER_ES);

            assertEquals(callee, calleeAuto);
            assertEquals(callee, calleeSM);
            assertEquals(callee, calleeSR);
            assertEquals(callee, calleeTS);
            assertEquals(callee, calleeES);
        }
        
        public static void runGetTypes1() {
            Class<?> callee = SI1.class;
            Class<?> caller = SI0.class;
                    
            List<Class<?>> listAuto = TraceUtils.getTypes();
            List<Class<?>> listSM   = TraceUtils.test_getTypes(TraceUtils.RESOLVER_SM);
            List<Class<?>> listSR   = TraceUtils.test_getTypes(TraceUtils.RESOLVER_SR);

            if(false) { //NOPMD
                dump(listAuto);
                dump(listSM);
                dump(listSR);
            }

            assertTrue(
                listAuto.equals(listSM) ||
                listAuto.equals(listSR)
            );

            assertEquals(listSM, listSR);

            assertTrue(listSM.size() > 5);

            assertEquals(callee, listSM.get(0));
            assertEquals(callee, listSR.get(0));
            
            assertEquals(caller, listSM.get(1));
            assertEquals(caller, listSR.get(1));
            
            assertThrows(UnsupportedOperationException.class, ()->{
                TraceUtils.test_getTypes(TraceUtils.RESOLVER_TS);
            });
            assertThrows(UnsupportedOperationException.class, ()->{
                TraceUtils.test_getTypes(TraceUtils.RESOLVER_ES);
            });
        }
        
        public static void runGetNames1() {
            String callee = SI1.class.getName();
            String caller = SI0.class.getName();
            
            List<String> listAuto = TraceUtils.getNames();
            List<String> listSM  = TraceUtils.test_getNames(TraceUtils.RESOLVER_SM);
            List<String> listSR  = TraceUtils.test_getNames(TraceUtils.RESOLVER_SR);
            List<String> listTS  = TraceUtils.test_getNames(TraceUtils.RESOLVER_TS);
            List<String> listES  = TraceUtils.test_getNames(TraceUtils.RESOLVER_ES);

            if(false) { //NOPMD
                dump(listAuto);
                dump(listSM);
                dump(listSR);
                dump(listTS);
                dump(listES);
            }

            assertTrue(
                listAuto.equals(listSM) ||
                listAuto.equals(listSR) ||
                listAuto.equals(listTS) ||
                listAuto.equals(listES) 
            );

            assertEquals(listSM, listSR);
            assertEquals(listTS, listES);

            assertTrue(listSM.size() > 5);
            assertTrue(listSM.size() <= listTS.size());

            assertEquals(callee, listSM.get(0));
            assertEquals(callee, listSR.get(0));
            assertEquals(callee, listTS.get(0));
            assertEquals(callee, listES.get(0));

            assertEquals(caller, listSM.get(1));
            assertEquals(caller, listSR.get(1));
            assertEquals(caller, listTS.get(1));
            assertEquals(caller, listES.get(1));
        }
        
        public static void runFrame1() {
            List<StackTraceElement> frames = TraceUtils.getFrames();
            List<String> names = TraceUtils.test_getNames(TraceUtils.RESOLVER_TS);
            
            if(false) { // NOPMD
                System.out.printf("frames = %d%n", frames.size());
                System.out.printf("names = %d%n", names.size());
                dump(frames);
                dump(names);
            }
            
            Wildcard callee = Wildcard.compile("net.ranides.assira.trace.TraceUtilsTest$SI1.runFrame1(TraceUtilsTest.java:*)");
            Wildcard caller = Wildcard.compile("net.ranides.assira.trace.TraceUtilsTest$SI0.runFrame0(TraceUtilsTest.java:*)");
            Wildcard frame2 = Wildcard.compile("net.ranides.assira.trace.TraceUtilsTest.testFrame(TraceUtilsTest.java:*)");

            assertTrue(callee.test(TraceUtils.getCalleeFrame().toString()));
            assertTrue(caller.test(TraceUtils.getCallerFrame().toString()));

            assertTrue(callee.test(frames.get(0).toString()));
            assertTrue(caller.test(frames.get(1).toString()));
            assertTrue(frame2.test(frames.get(2).toString()));

            assertEquals(frames.size(), names.size());
        }

    }
    
    @SuppressWarnings("PMD")
    private static void dump(List<?> list) {
        int i = 0;
        for(Object c : list) {
            System.err.printf("%d. %s%n", i++, c);
        }
        System.out.printf("%n");
    }
    
}
