package water.udf;

import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import java.io.InputStream;

import water.DKV;
import water.TestUtil;

import static water.udf.JFuncUtils.mockedRow;
import static water.udf.JFuncUtils.getSkippingClassloader;
import static water.udf.JFuncUtils.loadRawTestFunc;
import static water.udf.JFuncUtils.loadTestFunc;
import static water.util.ArrayUtils.sum;

public class JythonCFuncTest extends TestUtil {

  @BeforeClass
  static public void setup() {
    stall_till_cloudsize(1);
  }
  
  @Test
  public void testPyFunc2InvocationFromResources() throws Exception {
    String[] functionResources = ar("py/test_cfunc2.py", "py/__init__.py");
    CFuncRef
        cFuncRef = loadTestFunc("python", "test1.py", functionResources, "py.test_cfunc2.TestCFunc2");
    testPyFunc2Invocation(cFuncRef, functionResources);
  }

  @Test
  public void testPyFunc2InvocationFromString() throws Exception {
    // Load test python code
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try(InputStream is = cl.getResourceAsStream("py/test_cfunc2.py")) {
      byte[] ba = IOUtils.toByteArray(is);
      CFuncRef cFuncRef = loadRawTestFunc("python", "test2.py", "test.TestCFunc2", ba, "test.py");
      testPyFunc2Invocation(cFuncRef, new String[0]);
    }
  }

  private void testPyFunc2Invocation(CFuncRef testFuncDef, String[] resourcesToSkip) throws Exception {
    try {
      ClassLoader skippingCl = getSkippingClassloader(JythonCFuncTest.class.getClassLoader(),
                                                      new String[0], resourcesToSkip);
      ClassLoader cl = new DkvClassLoader(testFuncDef, skippingCl);
      JythonCFuncLoader loader = new JythonCFuncLoader();
      CFunc2 testFunc = loader.load(testFuncDef.funcName, CFunc2.class, cl);

      CBlock.CRow crow1 = mockedRow(10, 1.0);
      CBlock.CRow crow2 = mockedRow(5, 1.0);

      Assert.assertEquals("Test testFunc call should return expected value",
                          sum(crow1.readDoubles()) + sum(crow2.readDoubles()),
                          testFunc.apply(crow1, crow2), 1e-10);

    } finally {
      DKV.remove(testFuncDef.getKey());
    }
  }
}

@@ -1366,259 +1366,1360 @@
   %7D%0A
+  
 %0A  
-private void testPyFunc2Invocation(CFuncRef tes
+class DummyClassLoaderWrapper extends ClassLoader %7B%0A    private final ClassLoader inner;%0A%0A    public DummyClassLoaderWrapper(ClassLoader inner) %7B%0A      super(null);%0A      this.inner = inner;%0A    %7D%0A%0A    @Override%0A    protected Class%3C?%3E findClass(String name) throws ClassNo
 tF
+o
 un
-cDef, String%5B%5D resourcesToSkip) throws Exception %7B%0A    try %7B%0A      ClassLoader skippingCl = getSkippingClassloader(JythonCFuncTest.class.getClassLoader(),%0A                                          
+dException %7B%0A      return this.inner.loadClass(name);%0A    %7D%0A  %7D%0A%0A  @Test%0A  public void testJythonCFuncLoaderDeosNotRequireRelationshipBetweenClassLoaders() throws Exception %7B%0A    // Load test python code%0A    ClassLoader cl = Thread.currentThread().getContextClassLoader();%0A    try(InputStream is = cl.getResourceAsStream(%22py/test_cfunc2.py%22)) %7B%0A      byte%5B%5D ba = IOUtils.toByteArray(is);%0A      CFuncRef cFuncRef = loadRawTestFunc(%22python%22, %22test3.py%22, %22test3.TestCFunc2%22, ba, %22test3.py%22);%0A%0A      // Testing that a classloader that loaded objects of Jython library could be hierarchically independent on a class%0A      // loader that reads python artifacts (DKVClassLoader). Here we wrap a default class loader to break class loader chain.%0A      ClassLoader parentClassLoader = new DummyClassLoaderWrapper(cl);%0A      testPyFunc2Invocation(cFuncRef, parentClassLoader);%0A    %7D%0A  %7D%0A%0A  private void testPyFunc2Invocation(CFuncRef testFuncDef, String%5B%5D resourcesToSkip) %7B%0A    ClassLoader skippingCl = getSkippingClassloader(%0A            JythonCFuncTest.class.getClassLoader(),%0A
     
@@ -2740,16 +2740,28 @@
 ring%5B0%5D,
+%0A           
  resourc
@@ -2771,16 +2771,175 @@
 oSkip);%0A
+    testPyFunc2Invocation(testFuncDef, skippingCl);%0A  %7D%0A%0A  private void testPyFunc2Invocation(CFuncRef testFuncDef, ClassLoader parentClassLoader) %7B%0A    try %7B%0A
       Cl
@@ -2981,34 +2981,41 @@
 estFuncDef, 
-skippingCl
+parentClassLoader
 );%0A      Jyt

Add test demostrating a problem