http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d6203c51/zeppelin-zengine/src/main/java/org/apache/zeppelin/scheduler/RemoteScheduler.java ---------------------------------------------------------------------- diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/scheduler/RemoteScheduler.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/scheduler/RemoteScheduler.java new file mode 100644 index 0000000..ac9d536 --- /dev/null +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/scheduler/RemoteScheduler.java @@ -0,0 +1,390 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.zeppelin.scheduler; + +import org.apache.zeppelin.interpreter.InterpreterResult; +import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.remote.RemoteInterpreter; +import org.apache.zeppelin.scheduler.Job.Status; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ExecutorService; + +/** + * RemoteScheduler runs in ZeppelinServer and proxies Scheduler running on RemoteInterpreter + * + */ +public class RemoteScheduler implements Scheduler { + Logger logger = LoggerFactory.getLogger(RemoteScheduler.class); + + List<Job> queue = new LinkedList<>(); + List<Job> running = new LinkedList<>(); + private ExecutorService executor; + private SchedulerListener listener; + boolean terminate = false; + private String name; + private int maxConcurrency; + private final String sessionId; + private RemoteInterpreter remoteInterpreter; + + public RemoteScheduler(String name, ExecutorService executor, String sessionId, + RemoteInterpreter remoteInterpreter, SchedulerListener listener, + int maxConcurrency) { + this.name = name; + this.executor = executor; + this.listener = listener; + this.sessionId = sessionId; + this.remoteInterpreter = remoteInterpreter; + this.maxConcurrency = maxConcurrency; + } + + @Override + public void run() { + while (terminate == false) { + Job job = null; + + synchronized (queue) { + if (running.size() >= maxConcurrency || queue.isEmpty() == true) { + try { + queue.wait(500); + } catch (InterruptedException e) { + logger.error("Exception in RemoteScheduler while run queue.wait", e); + } + continue; + } + + job = queue.remove(0); + running.add(job); + } + + // run + Scheduler scheduler = this; + JobRunner jobRunner = new JobRunner(scheduler, job); + executor.execute(jobRunner); + + // wait until it is submitted to the remote + while (!jobRunner.isJobSubmittedInRemote()) { + synchronized (queue) { + try { + queue.wait(500); + } catch (InterruptedException e) { + logger.error("Exception in RemoteScheduler while jobRunner.isJobSubmittedInRemote " + + "queue.wait", e); + } + } + } + } + } + + @Override + public String getName() { + return name; + } + + @Override + public Collection<Job> getJobsWaiting() { + List<Job> ret = new LinkedList<>(); + synchronized (queue) { + for (Job job : queue) { + ret.add(job); + } + } + return ret; + } + + @Override + public Job removeFromWaitingQueue(String jobId) { + synchronized (queue) { + Iterator<Job> it = queue.iterator(); + while (it.hasNext()) { + Job job = it.next(); + if (job.getId().equals(jobId)) { + it.remove(); + return job; + } + } + } + return null; + } + + @Override + public Collection<Job> getJobsRunning() { + List<Job> ret = new LinkedList<>(); + synchronized (queue) { + for (Job job : running) { + ret.add(job); + } + } + return ret; + } + + @Override + public void submit(Job job) { + if (terminate) { + throw new RuntimeException("Scheduler already terminated"); + } + job.setStatus(Status.PENDING); + + synchronized (queue) { + queue.add(job); + queue.notify(); + } + } + + public void setMaxConcurrency(int maxConcurrency) { + this.maxConcurrency = maxConcurrency; + synchronized (queue) { + queue.notify(); + } + } + + /** + * Role of the class is get status info from remote process from PENDING to + * RUNNING status. + */ + private class JobStatusPoller extends Thread { + private long initialPeriodMsec; + private long initialPeriodCheckIntervalMsec; + private long checkIntervalMsec; + private volatile boolean terminate; + private JobListener listener; + private Job job; + volatile Status lastStatus; + + public JobStatusPoller(long initialPeriodMsec, + long initialPeriodCheckIntervalMsec, long checkIntervalMsec, Job job, + JobListener listener) { + setName("JobStatusPoller-" + job.getId()); + this.initialPeriodMsec = initialPeriodMsec; + this.initialPeriodCheckIntervalMsec = initialPeriodCheckIntervalMsec; + this.checkIntervalMsec = checkIntervalMsec; + this.job = job; + this.listener = listener; + this.terminate = false; + } + + @Override + public void run() { + long started = System.currentTimeMillis(); + while (terminate == false) { + long current = System.currentTimeMillis(); + long interval; + if (current - started < initialPeriodMsec) { + interval = initialPeriodCheckIntervalMsec; + } else { + interval = checkIntervalMsec; + } + + synchronized (this) { + try { + this.wait(interval); + } catch (InterruptedException e) { + logger.error("Exception in RemoteScheduler while run this.wait", e); + } + } + + if (terminate) { + // terminated by shutdown + break; + } + + Status newStatus = getStatus(); + if (newStatus == Status.UNKNOWN) { // unknown + continue; + } + + if (newStatus != Status.READY && newStatus != Status.PENDING) { + // we don't need more + break; + } + } + terminate = true; + } + + public void shutdown() { + terminate = true; + synchronized (this) { + this.notify(); + } + } + + + private Status getLastStatus() { + if (terminate == true) { + if (job.getErrorMessage() != null) { + return Status.ERROR; + } else if (lastStatus != Status.FINISHED && + lastStatus != Status.ERROR && + lastStatus != Status.ABORT) { + return Status.FINISHED; + } else { + return (lastStatus == null) ? Status.FINISHED : lastStatus; + } + } else { + return (lastStatus == null) ? Status.UNKNOWN : lastStatus; + } + } + + public synchronized Status getStatus() { + if (!remoteInterpreter.isOpened()) { + return getLastStatus(); + } + Status status = Status.valueOf(remoteInterpreter.getStatus(job.getId())); + if (status == Status.UNKNOWN) { + // not found this job in the remote schedulers. + // maybe not submitted, maybe already finished + //Status status = getLastStatus(); + listener.afterStatusChange(job, null, null); + return job.getStatus(); + } + lastStatus = status; + listener.afterStatusChange(job, null, status); + return status; + } + } + + //TODO(zjffdu) need to refactor the schdule module which is too complicated + private class JobRunner implements Runnable, JobListener { + private final Logger logger = LoggerFactory.getLogger(JobRunner.class); + private Scheduler scheduler; + private Job job; + private volatile boolean jobExecuted; + volatile boolean jobSubmittedRemotely; + + public JobRunner(Scheduler scheduler, Job job) { + this.scheduler = scheduler; + this.job = job; + jobExecuted = false; + jobSubmittedRemotely = false; + } + + public boolean isJobSubmittedInRemote() { + return jobSubmittedRemotely; + } + + @Override + public void run() { + if (job.isAborted()) { + synchronized (queue) { + job.setStatus(Status.ABORT); + job.aborted = false; + + running.remove(job); + queue.notify(); + } + jobSubmittedRemotely = true; + + return; + } + + JobStatusPoller jobStatusPoller = new JobStatusPoller(1500, 100, 500, + job, this); + jobStatusPoller.start(); + + if (listener != null) { + listener.jobStarted(scheduler, job); + } + job.run(); + + jobExecuted = true; + jobSubmittedRemotely = true; + + jobStatusPoller.shutdown(); + try { + jobStatusPoller.join(); + } catch (InterruptedException e) { + logger.error("JobStatusPoller interrupted", e); + } + + // set job status based on result. + Object jobResult = job.getReturn(); + if (job.isAborted()) { + job.setStatus(Status.ABORT); + } else if (job.getException() != null) { + logger.debug("Job ABORT, " + job.getId()); + job.setStatus(Status.ERROR); + } else if (jobResult != null && jobResult instanceof InterpreterResult + && ((InterpreterResult) jobResult).code() == Code.ERROR) { + logger.debug("Job Error, " + job.getId()); + job.setStatus(Status.ERROR); + } else { + logger.debug("Job Finished, " + job.getId()); + job.setStatus(Status.FINISHED); + } + + synchronized (queue) { + if (listener != null) { + listener.jobFinished(scheduler, job); + } + + // reset aborted flag to allow retry + job.aborted = false; + + running.remove(job); + queue.notify(); + } + } + + @Override + public void onProgressUpdate(Job job, int progress) { + } + + @Override + public void beforeStatusChange(Job job, Status before, Status after) { + } + + @Override + public void afterStatusChange(Job job, Status before, Status after) { + // Update remoteStatus + if (jobExecuted == false) { + if (after == Status.FINISHED || after == Status.ABORT + || after == Status.ERROR) { + // it can be status of last run. + // so not updating the remoteStatus + return; + } else if (after == Status.RUNNING) { + jobSubmittedRemotely = true; + job.setStatus(Status.RUNNING); + } + } else { + jobSubmittedRemotely = true; + } + + // only set status when it is RUNNING + // We would set other status based on the interpret result + if (after == Status.RUNNING) { + job.setStatus(Status.RUNNING); + } + } + } + + @Override + public void stop() { + terminate = true; + synchronized (queue) { + queue.notify(); + } + + } + +}
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d6203c51/zeppelin-zengine/src/main/java/org/apache/zeppelin/util/Util.java ---------------------------------------------------------------------- diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/util/Util.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/util/Util.java deleted file mode 100644 index be45b9e..0000000 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/util/Util.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.zeppelin.util; - -import org.apache.commons.lang3.StringUtils; - -import java.io.IOException; -import java.util.Properties; - -/** - * TODO(moon) : add description. - */ -public class Util { - private static final String PROJECT_PROPERTIES_VERSION_KEY = "version"; - private static final String GIT_PROPERTIES_COMMIT_ID_KEY = "git.commit.id.abbrev"; - private static final String GIT_PROPERTIES_COMMIT_TS_KEY = "git.commit.time"; - - private static Properties projectProperties; - private static Properties gitProperties; - - static { - projectProperties = new Properties(); - gitProperties = new Properties(); - try { - projectProperties.load(Util.class.getResourceAsStream("/project.properties")); - gitProperties.load(Util.class.getResourceAsStream("/git.properties")); - } catch (IOException e) { - //Fail to read project.properties - } - } - - /** - * Get Zeppelin version - * - * @return Current Zeppelin version - */ - public static String getVersion() { - return StringUtils.defaultIfEmpty(projectProperties.getProperty(PROJECT_PROPERTIES_VERSION_KEY), - StringUtils.EMPTY); - } - - /** - * Get Zeppelin Git latest commit id - * - * @return Latest Zeppelin commit id - */ - public static String getGitCommitId() { - return StringUtils.defaultIfEmpty(gitProperties.getProperty(GIT_PROPERTIES_COMMIT_ID_KEY), - StringUtils.EMPTY); - } - - /** - * Get Zeppelin Git latest commit timestamp - * - * @return Latest Zeppelin commit timestamp - */ - public static String getGitTimestamp() { - return StringUtils.defaultIfEmpty(gitProperties.getProperty(GIT_PROPERTIES_COMMIT_TS_KEY), - StringUtils.EMPTY); - } -} http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d6203c51/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java ---------------------------------------------------------------------- diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java index 305258a..bf49490 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java @@ -16,15 +16,18 @@ */ package org.apache.zeppelin.helium; -import com.google.common.collect.Maps; -import org.apache.commons.io.FileUtils; import org.apache.zeppelin.conf.ZeppelinConfiguration; -import org.apache.zeppelin.dep.Dependency; -import org.apache.zeppelin.dep.DependencyResolver; -import org.apache.zeppelin.interpreter.*; -import org.apache.zeppelin.interpreter.mock.MockInterpreter1; -import org.apache.zeppelin.interpreter.mock.MockInterpreter2; -import org.apache.zeppelin.notebook.*; +import org.apache.zeppelin.interpreter.AbstractInterpreterTest; +import org.apache.zeppelin.interpreter.Interpreter; +import org.apache.zeppelin.interpreter.InterpreterResultMessage; +import org.apache.zeppelin.interpreter.InterpreterSetting; +import org.apache.zeppelin.notebook.ApplicationState; +import org.apache.zeppelin.notebook.JobListenerFactory; +import org.apache.zeppelin.notebook.Note; +import org.apache.zeppelin.notebook.Notebook; +import org.apache.zeppelin.notebook.NotebookAuthorization; +import org.apache.zeppelin.notebook.Paragraph; +import org.apache.zeppelin.notebook.ParagraphJobListener; import org.apache.zeppelin.notebook.repo.VFSNotebookRepo; import org.apache.zeppelin.scheduler.Job; import org.apache.zeppelin.scheduler.SchedulerFactory; @@ -35,24 +38,16 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; -public class HeliumApplicationFactoryTest implements JobListenerFactory { - private File tmpDir; - private File notebookDir; - private ZeppelinConfiguration conf; +public class HeliumApplicationFactoryTest extends AbstractInterpreterTest implements JobListenerFactory { + private SchedulerFactory schedulerFactory; - private DependencyResolver depResolver; - private InterpreterFactory factory; - private InterpreterSettingManager interpreterSettingManager; private VFSNotebookRepo notebookRepo; private Notebook notebook; private HeliumApplicationFactory heliumAppFactory; @@ -60,46 +55,15 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory { @Before public void setUp() throws Exception { - tmpDir = new File(System.getProperty("java.io.tmpdir")+"/ZepelinLTest_"+System.currentTimeMillis()); - tmpDir.mkdirs(); - File confDir = new File(tmpDir, "conf"); - confDir.mkdirs(); - notebookDir = new File(tmpDir + "/notebook"); - notebookDir.mkdirs(); - - File home = new File(getClass().getClassLoader().getResource("note").getFile()) // zeppelin/zeppelin-zengine/target/test-classes/note - .getParentFile() // zeppelin/zeppelin-zengine/target/test-classes - .getParentFile() // zeppelin/zeppelin-zengine/target - .getParentFile() // zeppelin/zeppelin-zengine - .getParentFile(); // zeppelin - - System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_HOME.getVarName(), home.getAbsolutePath()); - System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_CONF_DIR.getVarName(), tmpDir.getAbsolutePath() + "/conf"); - System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_NOTEBOOK_DIR.getVarName(), notebookDir.getAbsolutePath()); - - conf = new ZeppelinConfiguration(); - - this.schedulerFactory = new SchedulerFactory(); + System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_INTERPRETER_GROUP_ORDER.getVarName(), "mock1,mock2"); + super.setUp(); + this.schedulerFactory = SchedulerFactory.singleton(); heliumAppFactory = new HeliumApplicationFactory(); - depResolver = new DependencyResolver(tmpDir.getAbsolutePath() + "/local-repo"); - interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true)); - factory = new InterpreterFactory(conf, null, null, heliumAppFactory, depResolver, false, interpreterSettingManager); - HashMap<String, String> env = new HashMap<>(); - env.put("ZEPPELIN_CLASSPATH", new File("./target/test-classes").getAbsolutePath()); - factory.setEnv(env); - - ArrayList<InterpreterInfo> interpreterInfos = new ArrayList<>(); - interpreterInfos.add(new InterpreterInfo(MockInterpreter1.class.getName(), "mock1", true, new HashMap<String, Object>())); - interpreterSettingManager.add("mock1", interpreterInfos, new ArrayList<Dependency>(), new InterpreterOption(), - Maps.<String, DefaultInterpreterProperty>newHashMap(), "mock1", null); - interpreterSettingManager.createNewSetting("mock1", "mock1", new ArrayList<Dependency>(), new InterpreterOption(true), new HashMap<String, InterpreterProperty>()); - - ArrayList<InterpreterInfo> interpreterInfos2 = new ArrayList<>(); - interpreterInfos2.add(new InterpreterInfo(MockInterpreter2.class.getName(), "mock2", true, new HashMap<String, Object>())); - interpreterSettingManager.add("mock2", interpreterInfos2, new ArrayList<Dependency>(), new InterpreterOption(), - Maps.<String, DefaultInterpreterProperty>newHashMap(), "mock2", null); - interpreterSettingManager.createNewSetting("mock2", "mock2", new ArrayList<Dependency>(), new InterpreterOption(), new HashMap<String, InterpreterProperty>()); + // set AppEventListener properly + for (InterpreterSetting interpreterSetting : interpreterSettingManager.get()) { + interpreterSetting.setAppEventListener(heliumAppFactory); + } SearchService search = mock(SearchService.class); notebookRepo = new VFSNotebookRepo(conf); @@ -108,7 +72,7 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory { conf, notebookRepo, schedulerFactory, - factory, + interpreterFactory, interpreterSettingManager, this, search, @@ -124,16 +88,7 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory { @After public void tearDown() throws Exception { - List<InterpreterSetting> settings = interpreterSettingManager.get(); - for (InterpreterSetting setting : settings) { - for (InterpreterGroup intpGroup : setting.getAllInterpreterGroups()) { - intpGroup.close(); - } - } - - FileUtils.deleteDirectory(tmpDir); - System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_CONF_DIR.getVarName(), - ZeppelinConfiguration.ConfVars.ZEPPELIN_CONF_DIR.getStringValue()); + super.tearDown(); } @@ -150,7 +105,7 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory { "", ""); Note note1 = notebook.createNote(anonymous); - interpreterSettingManager.setInterpreters("user", note1.getId(),interpreterSettingManager.getDefaultInterpreterSettingList()); + interpreterSettingManager.setInterpreterBinding("user", note1.getId(),interpreterSettingManager.getInterpreterSettingIds()); Paragraph p1 = note1.addNewParagraph(AuthenticationInfo.ANONYMOUS); @@ -196,7 +151,7 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory { "", ""); Note note1 = notebook.createNote(anonymous); - interpreterSettingManager.setInterpreters("user", note1.getId(), interpreterSettingManager.getDefaultInterpreterSettingList()); + interpreterSettingManager.setInterpreterBinding("user", note1.getId(), interpreterSettingManager.getInterpreterSettingIds()); Paragraph p1 = note1.addNewParagraph(AuthenticationInfo.ANONYMOUS); @@ -236,7 +191,7 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory { "", ""); Note note1 = notebook.createNote(anonymous); - notebook.bindInterpretersToNote("user", note1.getId(), interpreterSettingManager.getDefaultInterpreterSettingList()); + notebook.bindInterpretersToNote("user", note1.getId(), interpreterSettingManager.getInterpreterSettingIds()); Paragraph p1 = note1.addNewParagraph(AuthenticationInfo.ANONYMOUS); @@ -297,7 +252,7 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory { "", ""); Note note1 = notebook.createNote(anonymous); - notebook.bindInterpretersToNote("user", note1.getId(), interpreterSettingManager.getDefaultInterpreterSettingList()); + notebook.bindInterpretersToNote("user", note1.getId(), interpreterSettingManager.getInterpreterSettingIds()); String mock1IntpSettingId = null; for (InterpreterSetting setting : notebook.getBindedInterpreterSettings(note1.getId())) { if (setting.getName().equals("mock1")) { http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d6203c51/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumTest.java ---------------------------------------------------------------------- diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumTest.java index 6b4932d..bdd639e 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumTest.java @@ -52,7 +52,7 @@ public class HeliumTest { // given File heliumConf = new File(tmpDir, "helium.conf"); Helium helium = new Helium(heliumConf.getAbsolutePath(), localRegistryPath.getAbsolutePath(), - null, null, null); + null, null, null, null); assertFalse(heliumConf.exists()); // when @@ -63,14 +63,14 @@ public class HeliumTest { // then load without exception Helium heliumRestored = new Helium( - heliumConf.getAbsolutePath(), localRegistryPath.getAbsolutePath(), null, null, null); + heliumConf.getAbsolutePath(), localRegistryPath.getAbsolutePath(), null, null, null, null); } @Test public void testRestoreRegistryInstances() throws IOException, URISyntaxException, TaskRunnerException { File heliumConf = new File(tmpDir, "helium.conf"); Helium helium = new Helium( - heliumConf.getAbsolutePath(), localRegistryPath.getAbsolutePath(), null, null, null); + heliumConf.getAbsolutePath(), localRegistryPath.getAbsolutePath(), null, null, null, null); HeliumTestRegistry registry1 = new HeliumTestRegistry("r1", "r1"); HeliumTestRegistry registry2 = new HeliumTestRegistry("r2", "r2"); helium.addRegistry(registry1); @@ -105,7 +105,7 @@ public class HeliumTest { public void testRefresh() throws IOException, URISyntaxException, TaskRunnerException { File heliumConf = new File(tmpDir, "helium.conf"); Helium helium = new Helium( - heliumConf.getAbsolutePath(), localRegistryPath.getAbsolutePath(), null, null, null); + heliumConf.getAbsolutePath(), localRegistryPath.getAbsolutePath(), null, null, null, null); HeliumTestRegistry registry1 = new HeliumTestRegistry("r1", "r1"); helium.addRegistry(registry1); http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d6203c51/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/AbstractInterpreterTest.java ---------------------------------------------------------------------- diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/AbstractInterpreterTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/AbstractInterpreterTest.java new file mode 100644 index 0000000..21d7526 --- /dev/null +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/AbstractInterpreterTest.java @@ -0,0 +1,74 @@ +package org.apache.zeppelin.interpreter; + +import org.apache.commons.io.FileUtils; +import org.apache.zeppelin.conf.ZeppelinConfiguration; +import org.apache.zeppelin.display.AngularObjectRegistryListener; +import org.apache.zeppelin.helium.ApplicationEventListener; +import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcessListener; +import org.junit.After; +import org.junit.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +import static org.mockito.Mockito.mock; + + +/** + * This class will load configuration files under + * src/test/resources/interpreter + * src/test/resources/conf + * + * to construct InterpreterSettingManager and InterpreterFactory properly + * + */ +public abstract class AbstractInterpreterTest { + protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractInterpreterTest.class); + private static final String INTERPRETER_SCRIPT = + System.getProperty("os.name").startsWith("Windows") ? + "../bin/interpreter.cmd" : + "../bin/interpreter.sh"; + + protected InterpreterSettingManager interpreterSettingManager; + protected InterpreterFactory interpreterFactory; + protected File testRootDir; + protected File interpreterDir; + protected File confDir; + protected File notebookDir; + protected ZeppelinConfiguration conf; + + @Before + public void setUp() throws Exception { + // copy the resources files to a temp folder + testRootDir = new File(System.getProperty("java.io.tmpdir") + "/Zeppelin_Test_" + System.currentTimeMillis()); + testRootDir.mkdirs(); + LOGGER.info("Create tmp directory: {} as root folder of ZEPPELIN_INTERPRETER_DIR & ZEPPELIN_CONF_DIR", testRootDir.getAbsolutePath()); + interpreterDir = new File(testRootDir, "interpreter"); + confDir = new File(testRootDir, "conf"); + notebookDir = new File(testRootDir, "notebook"); + + interpreterDir.mkdirs(); + confDir.mkdirs(); + notebookDir.mkdirs(); + + FileUtils.copyDirectory(new File("src/test/resources/interpreter"), interpreterDir); + FileUtils.copyDirectory(new File("src/test/resources/conf"), confDir); + + System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_CONF_DIR.getVarName(), confDir.getAbsolutePath()); + System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_INTERPRETER_DIR.getVarName(), interpreterDir.getAbsolutePath()); + System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_NOTEBOOK_DIR.getVarName(), notebookDir.getAbsolutePath()); + System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_INTERPRETER_REMOTE_RUNNER.getVarName(), INTERPRETER_SCRIPT); + + conf = new ZeppelinConfiguration(); + interpreterSettingManager = new InterpreterSettingManager(conf, + mock(AngularObjectRegistryListener.class), mock(RemoteInterpreterProcessListener.class), mock(ApplicationEventListener.class)); + interpreterFactory = new InterpreterFactory(interpreterSettingManager); + } + + @After + public void tearDown() throws Exception { + interpreterSettingManager.close(); + FileUtils.deleteDirectory(testRootDir); + } +} http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d6203c51/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/DoubleEchoInterpreter.java ---------------------------------------------------------------------- diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/DoubleEchoInterpreter.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/DoubleEchoInterpreter.java new file mode 100644 index 0000000..be3d5be --- /dev/null +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/DoubleEchoInterpreter.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.zeppelin.interpreter; + +import java.util.Properties; + + +public class DoubleEchoInterpreter extends Interpreter { + + public DoubleEchoInterpreter(Properties property) { + super(property); + } + + @Override + public void open() { + + } + + @Override + public void close() { + + } + + @Override + public InterpreterResult interpret(String st, InterpreterContext context) { + return new InterpreterResult(InterpreterResult.Code.SUCCESS, st + "," + st); + } + + @Override + public void cancel(InterpreterContext context) { + + } + + @Override + public FormType getFormType() { + return null; + } + + @Override + public int getProgress(InterpreterContext context) { + return 0; + } +} http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d6203c51/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/EchoInterpreter.java ---------------------------------------------------------------------- diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/EchoInterpreter.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/EchoInterpreter.java new file mode 100644 index 0000000..e7a04f3 --- /dev/null +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/EchoInterpreter.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.zeppelin.interpreter; + +import java.util.Properties; + +/** + * Just return the received statement back + */ +public class EchoInterpreter extends Interpreter { + + public EchoInterpreter(Properties property) { + super(property); + } + + @Override + public void open() { + + } + + @Override + public void close() { + + } + + @Override + public InterpreterResult interpret(String st, InterpreterContext context) { + if (Boolean.parseBoolean(property.getProperty("zeppelin.interpreter.echo.fail", "false"))) { + return new InterpreterResult(InterpreterResult.Code.ERROR); + } else { + return new InterpreterResult(InterpreterResult.Code.SUCCESS, st); + } + } + + @Override + public void cancel(InterpreterContext context) { + + } + + @Override + public FormType getFormType() { + return FormType.NATIVE; + } + + @Override + public int getProgress(InterpreterContext context) { + return 0; + } +} http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d6203c51/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/InterpreterFactoryTest.java ---------------------------------------------------------------------- diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/InterpreterFactoryTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/InterpreterFactoryTest.java index aaa8864..f3137d9 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/InterpreterFactoryTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/InterpreterFactoryTest.java @@ -17,481 +17,50 @@ package org.apache.zeppelin.interpreter; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.NullArgumentException; -import org.apache.zeppelin.conf.ZeppelinConfiguration; -import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars; -import org.apache.zeppelin.dep.Dependency; -import org.apache.zeppelin.dep.DependencyResolver; -import org.apache.zeppelin.interpreter.mock.MockInterpreter1; -import org.apache.zeppelin.interpreter.mock.MockInterpreter2; import org.apache.zeppelin.interpreter.remote.RemoteInterpreter; -import org.apache.zeppelin.notebook.JobListenerFactory; -import org.apache.zeppelin.notebook.Note; -import org.apache.zeppelin.notebook.Notebook; -import org.apache.zeppelin.notebook.NotebookAuthorization; -import org.apache.zeppelin.notebook.repo.NotebookRepo; -import org.apache.zeppelin.notebook.repo.VFSNotebookRepo; -import org.apache.zeppelin.scheduler.SchedulerFactory; -import org.apache.zeppelin.search.SearchService; -import org.apache.zeppelin.user.AuthenticationInfo; -import org.junit.After; -import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; -import org.quartz.SchedulerException; -import org.sonatype.aether.RepositoryException; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import java.io.IOException; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class InterpreterFactoryTest { - - private InterpreterFactory factory; - private InterpreterSettingManager interpreterSettingManager; - private File tmpDir; - private ZeppelinConfiguration conf; - private InterpreterContext context; - private Notebook notebook; - private NotebookRepo notebookRepo; - private DependencyResolver depResolver; - private SchedulerFactory schedulerFactory; - private NotebookAuthorization notebookAuthorization; - @Mock - private JobListenerFactory jobListenerFactory; - - @Before - public void setUp() throws Exception { - tmpDir = new File(System.getProperty("java.io.tmpdir")+"/ZeppelinLTest_"+System.currentTimeMillis()); - tmpDir.mkdirs(); - new File(tmpDir, "conf").mkdirs(); - FileUtils.copyDirectory(new File("src/test/resources/interpreter"), new File(tmpDir, "interpreter")); - - System.setProperty(ConfVars.ZEPPELIN_HOME.getVarName(), tmpDir.getAbsolutePath()); - System.setProperty(ConfVars.ZEPPELIN_INTERPRETER_GROUP_ORDER.getVarName(), - "mock1,mock2,mock11,dev"); - conf = new ZeppelinConfiguration(); - schedulerFactory = new SchedulerFactory(); - depResolver = new DependencyResolver(tmpDir.getAbsolutePath() + "/local-repo"); - interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true)); - factory = new InterpreterFactory(conf, null, null, null, depResolver, false, interpreterSettingManager); - context = new InterpreterContext("note", "id", null, "title", "text", null, null, null, null, null, null, null); - - ArrayList<InterpreterInfo> interpreterInfos = new ArrayList<>(); - interpreterInfos.add(new InterpreterInfo(MockInterpreter1.class.getName(), "mock1", true, new HashMap<String, Object>())); - interpreterSettingManager.add("mock1", interpreterInfos, new ArrayList<Dependency>(), new InterpreterOption(), - Maps.<String, DefaultInterpreterProperty>newHashMap(), "mock1", null); - Map<String, InterpreterProperty> intp1Properties = new HashMap<String, InterpreterProperty>(); - intp1Properties.put("PROPERTY_1", - new InterpreterProperty("PROPERTY_1", "VALUE_1")); - intp1Properties.put("property_2", - new InterpreterProperty("property_2", "value_2")); - interpreterSettingManager.createNewSetting("mock1", "mock1", new ArrayList<Dependency>(), new InterpreterOption(true), intp1Properties); - - ArrayList<InterpreterInfo> interpreterInfos2 = new ArrayList<>(); - interpreterInfos2.add(new InterpreterInfo(MockInterpreter2.class.getName(), "mock2", true, new HashMap<String, Object>())); - interpreterSettingManager.add("mock2", interpreterInfos2, new ArrayList<Dependency>(), new InterpreterOption(), - Maps.<String, DefaultInterpreterProperty>newHashMap(), "mock2", null); - interpreterSettingManager.createNewSetting("mock2", "mock2", new ArrayList<Dependency>(), new InterpreterOption(), new HashMap<String, InterpreterProperty>()); - - SearchService search = mock(SearchService.class); - notebookRepo = new VFSNotebookRepo(conf); - notebookAuthorization = NotebookAuthorization.init(conf); - notebook = new Notebook(conf, notebookRepo, schedulerFactory, factory, interpreterSettingManager, jobListenerFactory, search, - notebookAuthorization, null); - } - @After - public void tearDown() throws Exception { - FileUtils.deleteDirectory(tmpDir); - } +public class InterpreterFactoryTest extends AbstractInterpreterTest { @Test - public void testBasic() { - List<InterpreterSetting> all = interpreterSettingManager.get(); - InterpreterSetting mock1Setting = null; - for (InterpreterSetting setting : all) { - if (setting.getName().equals("mock1")) { - mock1Setting = setting; - break; - } - } - -// mock1Setting = factory.createNewSetting("mock11", "mock1", new ArrayList<Dependency>(), new InterpreterOption(false), new Properties()); + public void testGetFactory() throws IOException { + // no default interpreter because there's no interpreter setting binded to this note + assertNull(interpreterFactory.getInterpreter("user1", "note1", "")); - InterpreterGroup interpreterGroup = mock1Setting.getInterpreterGroup("user", "sharedProcess"); - factory.createInterpretersForNote(mock1Setting, "user", "sharedProcess", "session"); + interpreterSettingManager.setInterpreterBinding("user1", "note1", interpreterSettingManager.getSettingIds()); + assertTrue(interpreterFactory.getInterpreter("user1", "note1", "") instanceof RemoteInterpreter); + RemoteInterpreter remoteInterpreter = (RemoteInterpreter) interpreterFactory.getInterpreter("user1", "note1", ""); + // EchoInterpreter is the default interpreter (see zeppelin-interpreter/src/test/resources/conf/interpreter.json) + assertEquals(EchoInterpreter.class.getName(), remoteInterpreter.getClassName()); - // get interpreter - assertNotNull("get Interpreter", interpreterGroup.get("session").get(0)); + assertTrue(interpreterFactory.getInterpreter("user1", "note1", "test") instanceof RemoteInterpreter); + remoteInterpreter = (RemoteInterpreter) interpreterFactory.getInterpreter("user1", "note1", "test"); + assertEquals(EchoInterpreter.class.getName(), remoteInterpreter.getClassName()); - // try to get unavailable interpreter - assertNull(interpreterSettingManager.get("unknown")); + assertTrue(interpreterFactory.getInterpreter("user1", "note1", "echo") instanceof RemoteInterpreter); + remoteInterpreter = (RemoteInterpreter) interpreterFactory.getInterpreter("user1", "note1", "echo"); + assertEquals(EchoInterpreter.class.getName(), remoteInterpreter.getClassName()); - // restart interpreter - interpreterSettingManager.restart(mock1Setting.getId()); - assertNull(mock1Setting.getInterpreterGroup("user", "sharedProcess").get("session")); + assertTrue(interpreterFactory.getInterpreter("user1", "note1", "double_echo") instanceof RemoteInterpreter); + remoteInterpreter = (RemoteInterpreter) interpreterFactory.getInterpreter("user1", "note1", "double_echo"); + assertEquals(DoubleEchoInterpreter.class.getName(), remoteInterpreter.getClassName()); } - @Test - public void testRemoteRepl() throws Exception { - interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true)); - ArrayList<InterpreterInfo> interpreterInfos = new ArrayList<>(); - interpreterInfos.add(new InterpreterInfo(MockInterpreter1.class.getName(), "mock1", true, new HashMap<String, Object>())); - interpreterSettingManager.add("mock1", interpreterInfos, new ArrayList<Dependency>(), new InterpreterOption(), - Maps.<String, DefaultInterpreterProperty>newHashMap(), "mock1", null); - Map<String, InterpreterProperty> intp1Properties = new HashMap<String, InterpreterProperty>(); - intp1Properties.put("PROPERTY_1", - new InterpreterProperty("PROPERTY_1", "VALUE_1")); - intp1Properties.put("property_2", new InterpreterProperty("property_2", "value_2")); - interpreterSettingManager.createNewSetting("mock1", "mock1", new ArrayList<Dependency>(), new InterpreterOption(true), intp1Properties); - factory = new InterpreterFactory(conf, null, null, null, depResolver, false, interpreterSettingManager); - List<InterpreterSetting> all = interpreterSettingManager.get(); - InterpreterSetting mock1Setting = null; - for (InterpreterSetting setting : all) { - if (setting.getName().equals("mock1")) { - mock1Setting = setting; - break; - } - } - InterpreterGroup interpreterGroup = mock1Setting.getInterpreterGroup("user", "sharedProcess"); - factory.createInterpretersForNote(mock1Setting, "user", "sharedProcess", "session"); - // get interpreter - assertNotNull("get Interpreter", interpreterGroup.get("session").get(0)); - assertTrue(interpreterGroup.get("session").get(0) instanceof LazyOpenInterpreter); - LazyOpenInterpreter lazyInterpreter = (LazyOpenInterpreter)(interpreterGroup.get("session").get(0)); - assertTrue(lazyInterpreter.getInnerInterpreter() instanceof RemoteInterpreter); - RemoteInterpreter remoteInterpreter = (RemoteInterpreter) lazyInterpreter.getInnerInterpreter(); - assertEquals("VALUE_1", remoteInterpreter.getEnv().get("PROPERTY_1")); - assertEquals("value_2", remoteInterpreter.getProperty("property_2")); + @Test(expected = InterpreterException.class) + public void testUnknownRepl1() throws IOException { + interpreterSettingManager.setInterpreterBinding("user1", "note1", interpreterSettingManager.getSettingIds()); + interpreterFactory.getInterpreter("user1", "note1", "test.unknown_repl"); } - /** - * 2 users' interpreters in scoped mode. Each user has one session. Restarting user1's interpreter - * won't affect user2's interpreter - * @throws Exception - */ @Test - public void testRestartInterpreterInScopedMode() throws Exception { - interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true)); - ArrayList<InterpreterInfo> interpreterInfos = new ArrayList<>(); - interpreterInfos.add(new InterpreterInfo(MockInterpreter1.class.getName(), "mock1", true, new HashMap<String, Object>())); - interpreterSettingManager.add("mock1", interpreterInfos, new ArrayList<Dependency>(), new InterpreterOption(), - Maps.<String, DefaultInterpreterProperty>newHashMap(), "mock1", null); - Map<String, InterpreterProperty> intp1Properties = new HashMap<String, InterpreterProperty>(); - intp1Properties.put("PROPERTY_1", - new InterpreterProperty("PROPERTY_1", "VALUE_1")); - intp1Properties.put("property_2", - new InterpreterProperty("property_2", "value_2")); - interpreterSettingManager.createNewSetting("mock1", "mock1", new ArrayList<Dependency>(), new InterpreterOption(true), intp1Properties); - factory = new InterpreterFactory(conf, null, null, null, depResolver, false, interpreterSettingManager); - List<InterpreterSetting> all = interpreterSettingManager.get(); - InterpreterSetting mock1Setting = null; - for (InterpreterSetting setting : all) { - if (setting.getName().equals("mock1")) { - mock1Setting = setting; - break; - } - } - mock1Setting.getOption().setPerUser("scoped"); - mock1Setting.getOption().setPerNote("shared"); - // set remote as false so that we won't create new remote interpreter process - mock1Setting.getOption().setRemote(false); - mock1Setting.getOption().setHost("localhost"); - mock1Setting.getOption().setPort(2222); - InterpreterGroup interpreterGroup = mock1Setting.getInterpreterGroup("user1", "sharedProcess"); - factory.createInterpretersForNote(mock1Setting, "user1", "sharedProcess", "user1"); - factory.createInterpretersForNote(mock1Setting, "user2", "sharedProcess", "user2"); - - LazyOpenInterpreter interpreter1 = (LazyOpenInterpreter)interpreterGroup.get("user1").get(0); - interpreter1.open(); - LazyOpenInterpreter interpreter2 = (LazyOpenInterpreter)interpreterGroup.get("user2").get(0); - interpreter2.open(); - - mock1Setting.closeAndRemoveInterpreterGroup("sharedProcess", "user1"); - assertFalse(interpreter1.isOpen()); - assertTrue(interpreter2.isOpen()); - } - - /** - * 2 users' interpreters in isolated mode. Each user has one interpreterGroup. Restarting user1's interpreter - * won't affect user2's interpreter - * @throws Exception - */ - @Test - public void testRestartInterpreterInIsolatedMode() throws Exception { - interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true)); - ArrayList<InterpreterInfo> interpreterInfos = new ArrayList<>(); - interpreterInfos.add(new InterpreterInfo(MockInterpreter1.class.getName(), "mock1", true, new HashMap<String, Object>())); - interpreterSettingManager.add("mock1", interpreterInfos, new ArrayList<Dependency>(), new InterpreterOption(), - Maps.<String, DefaultInterpreterProperty>newHashMap(), "mock1", null); - Map<String, InterpreterProperty> intp1Properties = new HashMap<String, InterpreterProperty>(); - intp1Properties.put("PROPERTY_1", - new InterpreterProperty("PROPERTY_1", "VALUE_1")); - intp1Properties.put("property_2", - new InterpreterProperty("property_2", "value_2")); - interpreterSettingManager.createNewSetting("mock1", "mock1", new ArrayList<Dependency>(), new InterpreterOption(true), intp1Properties); - factory = new InterpreterFactory(conf, null, null, null, depResolver, false, interpreterSettingManager); - List<InterpreterSetting> all = interpreterSettingManager.get(); - InterpreterSetting mock1Setting = null; - for (InterpreterSetting setting : all) { - if (setting.getName().equals("mock1")) { - mock1Setting = setting; - break; - } - } - mock1Setting.getOption().setPerUser("isolated"); - mock1Setting.getOption().setPerNote("shared"); - // set remote as false so that we won't create new remote interpreter process - mock1Setting.getOption().setRemote(false); - mock1Setting.getOption().setHost("localhost"); - mock1Setting.getOption().setPort(2222); - InterpreterGroup interpreterGroup1 = mock1Setting.getInterpreterGroup("user1", "note1"); - InterpreterGroup interpreterGroup2 = mock1Setting.getInterpreterGroup("user2", "note2"); - factory.createInterpretersForNote(mock1Setting, "user1", "note1", "shared_session"); - factory.createInterpretersForNote(mock1Setting, "user2", "note2", "shared_session"); - - LazyOpenInterpreter interpreter1 = (LazyOpenInterpreter)interpreterGroup1.get("shared_session").get(0); - interpreter1.open(); - LazyOpenInterpreter interpreter2 = (LazyOpenInterpreter)interpreterGroup2.get("shared_session").get(0); - interpreter2.open(); - - mock1Setting.closeAndRemoveInterpreterGroup("note1", "user1"); - assertFalse(interpreter1.isOpen()); - assertTrue(interpreter2.isOpen()); - } - - @Test - public void testFactoryDefaultList() throws IOException, RepositoryException { - // get default settings - List<String> all = interpreterSettingManager.getDefaultInterpreterSettingList(); - assertTrue(interpreterSettingManager.get().size() >= all.size()); - } - - @Test - public void testExceptions() throws InterpreterException, IOException, RepositoryException { - List<String> all = interpreterSettingManager.getDefaultInterpreterSettingList(); - // add setting with null option & properties expected nullArgumentException.class - try { - interpreterSettingManager.add("mock2", new ArrayList<InterpreterInfo>(), new LinkedList<Dependency>(), new InterpreterOption(false), Collections.EMPTY_MAP, "", null); - } catch(NullArgumentException e) { - assertEquals("Test null option" , e.getMessage(),new NullArgumentException("option").getMessage()); - } - try { - interpreterSettingManager.add("mock2", new ArrayList<InterpreterInfo>(), new LinkedList<Dependency>(), new InterpreterOption(false), Collections.EMPTY_MAP, "", null); - } catch (NullArgumentException e){ - assertEquals("Test null properties" , e.getMessage(),new NullArgumentException("properties").getMessage()); - } - } - - - @Test - public void testSaveLoad() throws IOException, RepositoryException { - // interpreter settings - int numInterpreters = interpreterSettingManager.get().size(); - - // check if file saved - assertTrue(new File(conf.getInterpreterSettingPath()).exists()); - - interpreterSettingManager.createNewSetting("new-mock1", "mock1", new LinkedList<Dependency>(), new InterpreterOption(false), new HashMap<String, InterpreterProperty>()); - assertEquals(numInterpreters + 1, interpreterSettingManager.get().size()); - - interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true)); - - /* - Current situation, if InterpreterSettinfRef doesn't have the key of InterpreterSetting, it would be ignored. - Thus even though interpreter.json have several interpreterSetting in that file, it would be ignored and would not be initialized from loadFromFile. - In this case, only "mock11" would be referenced from file under interpreter/mock, and "mock11" group would be initialized. - */ - // TODO(jl): Decide how to handle the know referenced interpreterSetting. - assertEquals(1, interpreterSettingManager.get().size()); - } - - @Test - public void testInterpreterSettingPropertyClass() throws IOException, RepositoryException { - // check if default interpreter reference's property type is map - Map<String, InterpreterSetting> interpreterSettingRefs = interpreterSettingManager.getAvailableInterpreterSettings(); - InterpreterSetting intpSetting = interpreterSettingRefs.get("mock1"); - Map<String, DefaultInterpreterProperty> intpProperties = - (Map<String, DefaultInterpreterProperty>) intpSetting.getProperties(); - assertTrue(intpProperties instanceof Map); - - // check if interpreter instance is saved as Properties in conf/interpreter.json file - Map<String, InterpreterProperty> properties = new HashMap<String, InterpreterProperty>(); - properties.put("key1", new InterpreterProperty("key1", "value1", "type1")); - properties.put("key2", new InterpreterProperty("key2", "value2", "type2")); - - interpreterSettingManager.createNewSetting("newMock", "mock1", new LinkedList<Dependency>(), new InterpreterOption(false), properties); - - String confFilePath = conf.getInterpreterSettingPath(); - byte[] encoded = Files.readAllBytes(Paths.get(confFilePath)); - String json = new String(encoded, "UTF-8"); - - InterpreterInfoSaving infoSaving = InterpreterInfoSaving.fromJson(json); - Map<String, InterpreterSetting> interpreterSettings = infoSaving.interpreterSettings; - for (String key : interpreterSettings.keySet()) { - InterpreterSetting setting = interpreterSettings.get(key); - if (setting.getName().equals("newMock")) { - assertEquals(setting.getProperties().toString(), properties.toString()); - } - } - } - - @Test - public void testInterpreterAliases() throws IOException, RepositoryException { - interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true)); - factory = new InterpreterFactory(conf, null, null, null, depResolver, false, interpreterSettingManager); - final InterpreterInfo info1 = new InterpreterInfo("className1", "name1", true, null); - final InterpreterInfo info2 = new InterpreterInfo("className2", "name1", true, null); - interpreterSettingManager.add("group1", new ArrayList<InterpreterInfo>() {{ - add(info1); - }}, new ArrayList<Dependency>(), new InterpreterOption(true), Collections.EMPTY_MAP, "/path1", null); - interpreterSettingManager.add("group2", new ArrayList<InterpreterInfo>(){{ - add(info2); - }}, new ArrayList<Dependency>(), new InterpreterOption(true), Collections.EMPTY_MAP, "/path2", null); - - final InterpreterSetting setting1 = interpreterSettingManager.createNewSetting("test-group1", "group1", new ArrayList<Dependency>(), new InterpreterOption(true), new HashMap<String, InterpreterProperty>()); - final InterpreterSetting setting2 = interpreterSettingManager.createNewSetting("test-group2", "group1", new ArrayList<Dependency>(), new InterpreterOption(true), new HashMap<String, InterpreterProperty>()); - - interpreterSettingManager.setInterpreters("user", "note", new ArrayList<String>() {{ - add(setting1.getId()); - add(setting2.getId()); - }}); - - assertEquals("className1", factory.getInterpreter("user1", "note", "test-group1").getClassName()); - assertEquals("className1", factory.getInterpreter("user1", "note", "group1").getClassName()); - } - - @Test - public void testMultiUser() throws IOException, RepositoryException { - interpreterSettingManager = new InterpreterSettingManager(conf, depResolver, new InterpreterOption(true)); - factory = new InterpreterFactory(conf, null, null, null, depResolver, true, interpreterSettingManager); - final InterpreterInfo info1 = new InterpreterInfo("className1", "name1", true, null); - interpreterSettingManager.add("group1", new ArrayList<InterpreterInfo>(){{ - add(info1); - }}, new ArrayList<Dependency>(), new InterpreterOption(true), Collections.EMPTY_MAP, "/path1", null); - - InterpreterOption perUserInterpreterOption = new InterpreterOption(true, InterpreterOption.ISOLATED, InterpreterOption.SHARED); - final InterpreterSetting setting1 = interpreterSettingManager.createNewSetting("test-group1", "group1", new ArrayList<Dependency>(), perUserInterpreterOption, new HashMap<String, InterpreterProperty>()); - - interpreterSettingManager.setInterpreters("user1", "note", new ArrayList<String>() {{ - add(setting1.getId()); - }}); - - interpreterSettingManager.setInterpreters("user2", "note", new ArrayList<String>() {{ - add(setting1.getId()); - }}); - - assertNotEquals(factory.getInterpreter("user1", "note", "test-group1"), factory.getInterpreter("user2", "note", "test-group1")); - } - - - @Test - public void testInvalidInterpreterSettingName() { - try { - interpreterSettingManager.createNewSetting("new.mock1", "mock1", new LinkedList<Dependency>(), new InterpreterOption(false), new HashMap<String, InterpreterProperty>()); - fail("expect fail because of invalid InterpreterSetting Name"); - } catch (IOException e) { - assertEquals("'.' is invalid for InterpreterSetting name.", e.getMessage()); - } - } - - - @Test - public void getEditorSetting() throws IOException, RepositoryException, SchedulerException { - List<String> intpIds = new ArrayList<>(); - for(InterpreterSetting intpSetting: interpreterSettingManager.get()) { - if (intpSetting.getName().startsWith("mock1")) { - intpIds.add(intpSetting.getId()); - } - } - Note note = notebook.createNote(intpIds, new AuthenticationInfo("anonymous")); - - Interpreter interpreter = factory.getInterpreter("user1", note.getId(), "mock11"); - // get editor setting from interpreter-setting.json - Map<String, Object> editor = interpreterSettingManager.getEditorSetting(interpreter, "user1", note.getId(), "mock11"); - assertEquals("java", editor.get("language")); - - // when interpreter is not loaded via interpreter-setting.json - // or editor setting doesn't exit - editor = interpreterSettingManager.getEditorSetting(factory.getInterpreter("user1", note.getId(), "mock1"),"user1", note.getId(), "mock1"); - assertEquals(null, editor.get("language")); - - // when interpreter is not bound to note - editor = interpreterSettingManager.getEditorSetting(factory.getInterpreter("user1", note.getId(), "mock11"),"user1", note.getId(), "mock2"); - assertEquals("text", editor.get("language")); - } - - @Test - public void registerCustomInterpreterRunner() throws IOException { - InterpreterSettingManager spyInterpreterSettingManager = spy(interpreterSettingManager); - - doNothing().when(spyInterpreterSettingManager).saveToFile(); - - ArrayList<InterpreterInfo> interpreterInfos1 = new ArrayList<>(); - interpreterInfos1.add(new InterpreterInfo("name1.class", "name1", true, Maps.<String, Object>newHashMap())); - - spyInterpreterSettingManager.add("normalGroup1", interpreterInfos1, Lists.<Dependency>newArrayList(), new InterpreterOption(true), Maps.<String, DefaultInterpreterProperty>newHashMap(), "/normalGroup1", null); - - spyInterpreterSettingManager.createNewSetting("normalGroup1", "normalGroup1", Lists.<Dependency>newArrayList(), new InterpreterOption(true), new HashMap<String, InterpreterProperty>()); - - ArrayList<InterpreterInfo> interpreterInfos2 = new ArrayList<>(); - interpreterInfos2.add(new InterpreterInfo("name1.class", "name1", true, Maps.<String, Object>newHashMap())); - - InterpreterRunner mockInterpreterRunner = mock(InterpreterRunner.class); - - when(mockInterpreterRunner.getPath()).thenReturn("custom-linux-path.sh"); - - spyInterpreterSettingManager.add("customGroup1", interpreterInfos2, Lists.<Dependency>newArrayList(), new InterpreterOption(true), Maps.<String, DefaultInterpreterProperty>newHashMap(), "/customGroup1", mockInterpreterRunner); - - spyInterpreterSettingManager.createNewSetting("customGroup1", "customGroup1", Lists.<Dependency>newArrayList(), new InterpreterOption(true), new HashMap<String, InterpreterProperty>()); - - spyInterpreterSettingManager.setInterpreters("anonymous", "noteCustome", spyInterpreterSettingManager.getDefaultInterpreterSettingList()); - - factory.getInterpreter("anonymous", "noteCustome", "customGroup1"); - - verify(mockInterpreterRunner, times(1)).getPath(); - } - - @Test - public void interpreterRunnerTest() { - InterpreterRunner mockInterpreterRunner = mock(InterpreterRunner.class); - String testInterpreterRunner = "relativePath.sh"; - when(mockInterpreterRunner.getPath()).thenReturn(testInterpreterRunner); // This test only for Linux - Interpreter i = factory.createRemoteRepl("path1", "sessionKey", "className", new Properties(), interpreterSettingManager.get().get(0).getId(), "userName", false, mockInterpreterRunner); - String interpreterRunner = ((RemoteInterpreter) ((LazyOpenInterpreter) i).getInnerInterpreter()).getInterpreterRunner(); - assertNotEquals(interpreterRunner, testInterpreterRunner); - - testInterpreterRunner = "/AbsolutePath.sh"; - when(mockInterpreterRunner.getPath()).thenReturn(testInterpreterRunner); - i = factory.createRemoteRepl("path1", "sessionKey", "className", new Properties(), interpreterSettingManager.get().get(0).getId(), "userName", false, mockInterpreterRunner); - interpreterRunner = ((RemoteInterpreter) ((LazyOpenInterpreter) i).getInnerInterpreter()).getInterpreterRunner(); - assertEquals(interpreterRunner, testInterpreterRunner); + public void testUnknownRepl2() throws IOException { + interpreterSettingManager.setInterpreterBinding("user1", "note1", interpreterSettingManager.getSettingIds()); + assertNull(interpreterFactory.getInterpreter("user1", "note1", "unknown_repl")); } } http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d6203c51/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/InterpreterSettingManagerTest.java ---------------------------------------------------------------------- diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/InterpreterSettingManagerTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/InterpreterSettingManagerTest.java new file mode 100644 index 0000000..0bcdb6f --- /dev/null +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/InterpreterSettingManagerTest.java @@ -0,0 +1,286 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.zeppelin.interpreter; + +import org.apache.zeppelin.conf.ZeppelinConfiguration; +import org.apache.zeppelin.dep.Dependency; +import org.apache.zeppelin.display.AngularObjectRegistryListener; +import org.apache.zeppelin.helium.ApplicationEventListener; +import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcessListener; +import org.junit.Test; +import org.sonatype.aether.RepositoryException; +import org.sonatype.aether.repository.RemoteRepository; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; + + +public class InterpreterSettingManagerTest extends AbstractInterpreterTest { + + @Test + public void testInitInterpreterSettingManager() throws IOException, RepositoryException { + assertEquals(5, interpreterSettingManager.get().size()); + InterpreterSetting interpreterSetting = interpreterSettingManager.getByName("test"); + assertEquals("test", interpreterSetting.getName()); + assertEquals("test", interpreterSetting.getGroup()); + assertEquals(2, interpreterSetting.getInterpreterInfos().size()); + // 3 other builtin properties: + // * zeppelin.interpeter.output.limit + // * zeppelin.interpreter.localRepo + // * zeppelin.interpreter.max.poolsize + assertEquals(6, interpreterSetting.getJavaProperties().size()); + assertEquals("value_1", interpreterSetting.getJavaProperties().getProperty("property_1")); + assertEquals("new_value_2", interpreterSetting.getJavaProperties().getProperty("property_2")); + assertEquals("value_3", interpreterSetting.getJavaProperties().getProperty("property_3")); + assertEquals("shared", interpreterSetting.getOption().perNote); + assertEquals("shared", interpreterSetting.getOption().perUser); + assertEquals(0, interpreterSetting.getDependencies().size()); + assertNotNull(interpreterSetting.getAngularObjectRegistryListener()); + assertNotNull(interpreterSetting.getRemoteInterpreterProcessListener()); + assertNotNull(interpreterSetting.getAppEventListener()); + assertNotNull(interpreterSetting.getDependencyResolver()); + assertNotNull(interpreterSetting.getInterpreterSettingManager()); + assertEquals("linux_runner", interpreterSetting.getInterpreterRunner().getPath()); + + List<RemoteRepository> repositories = interpreterSettingManager.getRepositories(); + assertEquals(2, repositories.size()); + assertEquals("central", repositories.get(0).getId()); + + // Load it again + InterpreterSettingManager interpreterSettingManager2 = new InterpreterSettingManager(conf, + mock(AngularObjectRegistryListener.class), mock(RemoteInterpreterProcessListener.class), mock(ApplicationEventListener.class)); + assertEquals(5, interpreterSettingManager2.get().size()); + interpreterSetting = interpreterSettingManager2.getByName("test"); + assertEquals("test", interpreterSetting.getName()); + assertEquals("test", interpreterSetting.getGroup()); + assertEquals(2, interpreterSetting.getInterpreterInfos().size()); + assertEquals(6, interpreterSetting.getJavaProperties().size()); + assertEquals("value_1", interpreterSetting.getJavaProperties().getProperty("property_1")); + assertEquals("new_value_2", interpreterSetting.getJavaProperties().getProperty("property_2")); + assertEquals("value_3", interpreterSetting.getJavaProperties().getProperty("property_3")); + assertEquals("shared", interpreterSetting.getOption().perNote); + assertEquals("shared", interpreterSetting.getOption().perUser); + assertEquals("linux_runner", interpreterSetting.getInterpreterRunner().getPath()); + assertEquals(0, interpreterSetting.getDependencies().size()); + + repositories = interpreterSettingManager2.getRepositories(); + assertEquals(2, repositories.size()); + assertEquals("central", repositories.get(0).getId()); + + } + + @Test + public void testCreateUpdateRemoveSetting() throws IOException { + // create new interpreter setting + InterpreterOption option = new InterpreterOption(); + option.setPerNote("scoped"); + option.setPerUser("scoped"); + Map<String, InterpreterProperty> properties = new HashMap<>(); + properties.put("property_4", new InterpreterProperty("property_4","value_4")); + + try { + interpreterSettingManager.createNewSetting("test2", "test", new ArrayList<Dependency>(), option, properties); + fail("Should fail due to interpreter already existed"); + } catch (IOException e) { + assertTrue(e.getMessage().contains("already existed")); + } + + interpreterSettingManager.createNewSetting("test3", "test", new ArrayList<Dependency>(), option, properties); + assertEquals(6, interpreterSettingManager.get().size()); + InterpreterSetting interpreterSetting = interpreterSettingManager.getByName("test3"); + assertEquals("test3", interpreterSetting.getName()); + assertEquals("test", interpreterSetting.getGroup()); + // 3 other builtin properties: + // * zeppelin.interpeter.output.limit + // * zeppelin.interpreter.localRepo + // * zeppelin.interpreter.max.poolsize + assertEquals(4, interpreterSetting.getJavaProperties().size()); + assertEquals("value_4", interpreterSetting.getJavaProperties().getProperty("property_4")); + assertEquals("scoped", interpreterSetting.getOption().perNote); + assertEquals("scoped", interpreterSetting.getOption().perUser); + assertEquals(0, interpreterSetting.getDependencies().size()); + assertNotNull(interpreterSetting.getAngularObjectRegistryListener()); + assertNotNull(interpreterSetting.getRemoteInterpreterProcessListener()); + assertNotNull(interpreterSetting.getAppEventListener()); + assertNotNull(interpreterSetting.getDependencyResolver()); + assertNotNull(interpreterSetting.getInterpreterSettingManager()); + + // load it again, it should be saved in interpreter-setting.json. So we can restore it properly + InterpreterSettingManager interpreterSettingManager2 = new InterpreterSettingManager(conf, + mock(AngularObjectRegistryListener.class), mock(RemoteInterpreterProcessListener.class), mock(ApplicationEventListener.class)); + assertEquals(6, interpreterSettingManager2.get().size()); + interpreterSetting = interpreterSettingManager2.getByName("test3"); + assertEquals("test3", interpreterSetting.getName()); + assertEquals("test", interpreterSetting.getGroup()); + assertEquals(6, interpreterSetting.getJavaProperties().size()); + assertEquals("value_4", interpreterSetting.getJavaProperties().getProperty("property_4")); + assertEquals("scoped", interpreterSetting.getOption().perNote); + assertEquals("scoped", interpreterSetting.getOption().perUser); + assertEquals(0, interpreterSetting.getDependencies().size()); + + // update interpreter setting + InterpreterOption newOption = new InterpreterOption(); + newOption.setPerNote("scoped"); + newOption.setPerUser("isolated"); + Map<String, InterpreterProperty> newProperties = new HashMap<>(properties); + newProperties.put("property_4", new InterpreterProperty("property_4", "new_value_4")); + List<Dependency> newDependencies = new ArrayList<>(); + newDependencies.add(new Dependency("com.databricks:spark-avro_2.11:3.1.0")); + interpreterSettingManager.setPropertyAndRestart(interpreterSetting.getId(), newOption, newProperties, newDependencies); + interpreterSetting = interpreterSettingManager.get(interpreterSetting.getId()); + assertEquals("test3", interpreterSetting.getName()); + assertEquals("test", interpreterSetting.getGroup()); + assertEquals(4, interpreterSetting.getJavaProperties().size()); + assertEquals("new_value_4", interpreterSetting.getJavaProperties().getProperty("property_4")); + assertEquals("scoped", interpreterSetting.getOption().perNote); + assertEquals("isolated", interpreterSetting.getOption().perUser); + assertEquals(1, interpreterSetting.getDependencies().size()); + assertNotNull(interpreterSetting.getAngularObjectRegistryListener()); + assertNotNull(interpreterSetting.getRemoteInterpreterProcessListener()); + assertNotNull(interpreterSetting.getAppEventListener()); + assertNotNull(interpreterSetting.getDependencyResolver()); + assertNotNull(interpreterSetting.getInterpreterSettingManager()); + + // restart in note page + interpreterSettingManager.setInterpreterBinding("user1", "note1", interpreterSettingManager.getSettingIds()); + interpreterSettingManager.setInterpreterBinding("user2", "note2", interpreterSettingManager.getSettingIds()); + interpreterSettingManager.setInterpreterBinding("user3", "note3", interpreterSettingManager.getSettingIds()); + // create 3 sessions as it is scoped mode + interpreterSetting.getOption().setPerUser("scoped"); + interpreterSetting.getDefaultInterpreter("user1", "note1"); + interpreterSetting.getDefaultInterpreter("user2", "note2"); + interpreterSetting.getDefaultInterpreter("user3", "note3"); + InterpreterGroup interpreterGroup = interpreterSetting.getInterpreterGroup("user1", "note1"); + assertEquals(3, interpreterGroup.getSessionNum()); + // only close user1's session + interpreterSettingManager.restart(interpreterSetting.getId(), "note1", "user1"); + assertEquals(2, interpreterGroup.getSessionNum()); + // close all the sessions + interpreterSettingManager.restart(interpreterSetting.getId(), "note1", "anonymous"); + assertEquals(0, interpreterGroup.getSessionNum()); + + // remove interpreter setting + interpreterSettingManager.remove(interpreterSetting.getId()); + assertEquals(5, interpreterSettingManager.get().size()); + + // load it again + InterpreterSettingManager interpreterSettingManager3 = new InterpreterSettingManager(new ZeppelinConfiguration(), + mock(AngularObjectRegistryListener.class), mock(RemoteInterpreterProcessListener.class), mock(ApplicationEventListener.class)); + assertEquals(5, interpreterSettingManager3.get().size()); + + } + + @Test + public void testInterpreterBinding() throws IOException { + assertNull(interpreterSettingManager.getInterpreterBinding("note1")); + interpreterSettingManager.setInterpreterBinding("user1", "note1", interpreterSettingManager.getInterpreterSettingIds()); + assertEquals(interpreterSettingManager.getInterpreterSettingIds(), interpreterSettingManager.getInterpreterBinding("note1")); + } + + @Test + public void testUpdateInterpreterBinding_PerNoteShared() throws IOException { + InterpreterSetting defaultInterpreterSetting = interpreterSettingManager.get().get(0); + defaultInterpreterSetting.getOption().setPerNote("shared"); + + interpreterSettingManager.setInterpreterBinding("user1", "note1", interpreterSettingManager.getInterpreterSettingIds()); + // create interpreter of the first binded interpreter setting + interpreterFactory.getInterpreter("user1", "note1", ""); + assertEquals(1, defaultInterpreterSetting.getAllInterpreterGroups().size()); + + // choose the first setting + List<String> newSettingIds = new ArrayList<>(); + newSettingIds.add(interpreterSettingManager.getInterpreterSettingIds().get(1)); + + interpreterSettingManager.setInterpreterBinding("user1", "note1", newSettingIds); + assertEquals(newSettingIds, interpreterSettingManager.getInterpreterBinding("note1")); + // InterpreterGroup will still be alive as it is shared + assertEquals(1, defaultInterpreterSetting.getAllInterpreterGroups().size()); + } + + @Test + public void testUpdateInterpreterBinding_PerNoteIsolated() throws IOException { + InterpreterSetting defaultInterpreterSetting = interpreterSettingManager.get().get(0); + defaultInterpreterSetting.getOption().setPerNote("isolated"); + + interpreterSettingManager.setInterpreterBinding("user1", "note1", interpreterSettingManager.getInterpreterSettingIds()); + // create interpreter of the first binded interpreter setting + interpreterFactory.getInterpreter("user1", "note1", ""); + assertEquals(1, defaultInterpreterSetting.getAllInterpreterGroups().size()); + + // choose the first setting + List<String> newSettingIds = new ArrayList<>(); + newSettingIds.add(interpreterSettingManager.getInterpreterSettingIds().get(1)); + + interpreterSettingManager.setInterpreterBinding("user1", "note1", newSettingIds); + assertEquals(newSettingIds, interpreterSettingManager.getInterpreterBinding("note1")); + // InterpreterGroup will be closed as it is only belong to this note + assertEquals(0, defaultInterpreterSetting.getAllInterpreterGroups().size()); + + } + + @Test + public void testUpdateInterpreterBinding_PerNoteScoped() throws IOException { + InterpreterSetting defaultInterpreterSetting = interpreterSettingManager.get().get(0); + defaultInterpreterSetting.getOption().setPerNote("scoped"); + + interpreterSettingManager.setInterpreterBinding("user1", "note1", interpreterSettingManager.getInterpreterSettingIds()); + interpreterSettingManager.setInterpreterBinding("user1", "note2", interpreterSettingManager.getInterpreterSettingIds()); + // create 2 interpreter of the first binded interpreter setting for note1 and note2 + interpreterFactory.getInterpreter("user1", "note1", ""); + interpreterFactory.getInterpreter("user1", "note2", ""); + assertEquals(1, defaultInterpreterSetting.getAllInterpreterGroups().size()); + assertEquals(2, defaultInterpreterSetting.getAllInterpreterGroups().get(0).getSessionNum()); + + // choose the first setting + List<String> newSettingIds = new ArrayList<>(); + newSettingIds.add(interpreterSettingManager.getInterpreterSettingIds().get(1)); + + interpreterSettingManager.setInterpreterBinding("user1", "note1", newSettingIds); + assertEquals(newSettingIds, interpreterSettingManager.getInterpreterBinding("note1")); + // InterpreterGroup will be still alive but session belong to note1 will be closed + assertEquals(1, defaultInterpreterSetting.getAllInterpreterGroups().size()); + assertEquals(1, defaultInterpreterSetting.getAllInterpreterGroups().get(0).getSessionNum()); + + } + + @Test + public void testGetEditor() throws IOException { + interpreterSettingManager.setInterpreterBinding("user1", "note1", interpreterSettingManager.getInterpreterSettingIds()); + Interpreter echoInterpreter = interpreterFactory.getInterpreter("user1", "note1", "test.echo"); + // get editor setting from interpreter-setting.json + Map<String, Object> editor = interpreterSettingManager.getEditorSetting(echoInterpreter, "user1", "note1", "test.echo"); + assertEquals("java", editor.get("language")); + + // when editor setting doesn't exit, return the default editor + Interpreter mock1Interpreter = interpreterFactory.getInterpreter("user1", "note1", "mock1"); + editor = interpreterSettingManager.getEditorSetting(mock1Interpreter,"user1", "note1", "mock1"); + assertEquals("text", editor.get("language")); + + } +}