<commit_before>/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.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://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs.scheduler;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Assert;
import org.junit.Test;
import org.openmrs.api.context.Context;
import org.openmrs.scheduler.tasks.AbstractTask;
import org.openmrs.test.BaseContextSensitiveTest;
import org.openmrs.util.OpenmrsClassLoader;
import org.openmrs.util.PrivilegeConstants;

/**
 * TODO test all methods in ScheduleService
 */
public class SchedulerServiceTest extends BaseContextSensitiveTest {
	
	private static Log log = LogFactory.getLog(SchedulerServiceTest.class);

	@Test
	public void shouldResolveValidTaskClass() throws Exception {
		String className = "org.openmrs.scheduler.tasks.TestTask";
		Class c = OpenmrsClassLoader.getInstance().loadClass(className);
		Object o = c.newInstance();
		if (o instanceof Task)
			assertTrue("Class " + className + " is a valid Task", true);
		else
			fail("Class " + className + " is not a valid Task");
	}
	
	@Test(expected = ClassNotFoundException.class)
	public void shouldNotResolveInvalidClass() throws Exception {
		String className = "org.openmrs.scheduler.tasks.InvalidTask";
		Class c = OpenmrsClassLoader.getInstance().loadClass(className);
		Object o = c.newInstance();
		if (o instanceof Task)
			fail("Class " + className + " is not supposed to be a valid Task");
		else
			assertTrue("Class " + className + " is not a valid Task", true);
	}
	
	private static List<String> outputForConcurrentTasks = new ArrayList<String>();
	
	/**
	 * Longer running class used to demonstrate tasks running concurrently
	 */
	static class ExecutePrintingTask extends AbstractTask {
		
		public void execute() {
			synchronized (outputForConcurrentTasks) {
				outputForConcurrentTasks.add(getTaskDefinition().getProperty("id"));
			}
			try {
				Thread.sleep(Integer.valueOf(getTaskDefinition().getProperty("delay")));
			}
			catch (InterruptedException e) {
				log.error("Error generated", e);
			}
			synchronized (outputForConcurrentTasks) {
				outputForConcurrentTasks.add(getTaskDefinition().getProperty("id"));
			}
		}
	}
	
	/**
	 * Demonstrates concurrent running for tasks
	 * 
	 * <pre>
	 *             |
	 * SampleTask2 |    ----
	 * SampleTask1 |------------
	 *             |_____________ time
	 *              ^   ^   ^   ^
	 * Output:     S-1 S-2 E-2 E-1
	 * </pre>
	 */
	@Test
	public void shouldAllowTwoTasksToRunConcurrently() throws Exception {
		SchedulerService schedulerService = Context.getSchedulerService();
		
		TaskDefinition t1 = new TaskDefinition();
		t1.setId(1);
		t1.setStartOnStartup(false);
		t1.setStartTime(null);
		t1.setTaskClass(ExecutePrintingTask.class.getName());
		t1.setProperty("id", "TASK-1");
		t1.setProperty("delay", "400"); // must be longer than t2's delay
		
		TaskDefinition t2 = new TaskDefinition();
		t2.setId(2);
		t2.setStartOnStartup(false);
		t2.setStartTime(null);
		t2.setTaskClass(ExecutePrintingTask.class.getName());
		t2.setProperty("id", "TASK-2");
		t2.setProperty("delay", "100"); // must be shorter than t1's delay
		
		schedulerService.scheduleTask(t1);
		Thread.sleep(50); // so t2 doesn't start before t1 due to random millisecond offsets
		schedulerService.scheduleTask(t2);
		Thread.sleep(600); // must be longer than t2's delay
		assertEquals(Arrays.asList("TASK-1", "TASK-2", "TASK-2", "TASK-1"), outputForConcurrentTasks);
	}
	
	private static List<String> outputForConcurrentInit = new ArrayList<String>();
	
	/**
	 * Longer init'ing class for concurrent init test
	 */
	static class SimpleTask extends AbstractTask {
		
		public void initialize(TaskDefinition config) {
			synchronized (outputForConcurrentInit) {
				outputForConcurrentInit.add(config.getProperty("id"));
			}
			super.initialize(config);
			try {
				// must be less than delay before printing
				Thread.sleep(Integer.valueOf(config.getProperty("delay")));
			}
			catch (InterruptedException e) {
				log.error("Error generated", e);
			}
			synchronized (outputForConcurrentInit) {
				outputForConcurrentInit.add(config.getProperty("id"));
			}
		}
		
		public void execute() {
		}
	}
	
	/**
	 * Demonstrates concurrent initializing for tasks
	 * 
	 * <pre>
	 *             |
	 * SampleTask4 |    ----
	 * SampleTask3 |------------
	 *             |_____________ time
	 *              ^   ^   ^   ^
	 * Output:     S-3 S-4 E-4 E-3
	 * </pre>
	 */
	@Test
	public void shouldAllowTwoTasksInitMethodsToRunConcurrently() throws Exception {
		SchedulerService schedulerService = Context.getSchedulerService();
		
		TaskDefinition t3 = new TaskDefinition();
		t3.setId(3);
		t3.setStartOnStartup(false);
		t3.setStartTime(null); // so it starts immediately
		t3.setTaskClass(SimpleTask.class.getName());
		t3.setProperty("id", "TASK-3");
		t3.setProperty("delay", "300"); // must be longer than t4's delay
		
		TaskDefinition t4 = new TaskDefinition();
		t4.setId(4);
		t4.setStartOnStartup(false);
		t4.setStartTime(null); // so it starts immediately
		t4.setTaskClass(SimpleTask.class.getName());
		t4.setProperty("id", "TASK-4");
		t4.setProperty("delay", "100");
		
		// both of these tasks start immediately
		schedulerService.scheduleTask(t3); // starts first, ends last
		schedulerService.scheduleTask(t4); // starts last, ends first
		Thread.sleep(500); // must be greater than task3 delay so that it prints out its end
		assertEquals(Arrays.asList("TASK-3", "TASK-4", "TASK-4", "TASK-3"), outputForConcurrentInit);
		
		// cleanup
		schedulerService.shutdownTask(t3);
		schedulerService.shutdownTask(t4);
	}
	
	private static List<String> outputForInitExecSync = new ArrayList<String>();
	
	static class SampleTask5 extends AbstractTask {
		
		public void initialize(TaskDefinition config) {
			synchronized (outputForInitExecSync) {
				outputForInitExecSync.add("INIT-START-5");
			}
			super.initialize(config);
			try {
				Thread.sleep(700);
			}
			catch (InterruptedException e) {
				log.error("Error generated", e);
			}
			synchronized (outputForInitExecSync) {
				outputForInitExecSync.add("INIT-END-5");
			}
		}
		
		public void execute() {
			synchronized (outputForInitExecSync) {
				outputForInitExecSync.add("IN EXECUTE");
			}
		}
	}
	
	/**
	 * Demonstrates that initialization of a task is accomplished before its execution without
	 * interleaving, which is a non-trivial behavior in the presence of a threaded initialization
	 * method (as implemented in TaskThreadedInitializationWrapper)
	 * 
	 * <pre>
	 *             |
	 * SampleTask5 |------------
	 *             |_____________ time
	 *              ^   ^   ^   ^
	 * Output:     IS  IE   S   E
	 * </pre>
	 */
	@Test
	public void shouldNotAllowTaskExecuteToRunBeforeInitializationIsComplete() throws Exception {
		SchedulerService schedulerService = Context.getSchedulerService();
		
		TaskDefinition t5 = new TaskDefinition();
		t5.setId(5);
		t5.setStartOnStartup(false);
		t5.setStartTime(null); // immediate start
		t5.setTaskClass(SampleTask5.class.getName());
		
		schedulerService.scheduleTask(t5);
		Thread.sleep(1200);
		assertEquals(Arrays.asList("INIT-START-5", "INIT-END-5", "IN EXECUTE"), outputForInitExecSync);
	}
	
	@Test
	public void saveTask_shouldSaveTaskToTheDatabase() throws Exception {
		SchedulerService service = Context.getSchedulerService();
		Assert.assertEquals(0, service.getRegisteredTasks().size());
		
		TaskDefinition def = new TaskDefinition();
		final String TASK_NAME = "This is my test! 123459876";
		def.setName(TASK_NAME);
		def.setStartOnStartup(false);
		def.setRepeatInterval(10L);
		def.setTaskClass(ExecutePrintingTask.class.getName());
		service.saveTask(def);
		Assert.assertEquals(1, service.getRegisteredTasks().size());
		
		def = service.getTaskByName(TASK_NAME);
		Assert.assertEquals(Context.getAuthenticatedUser().getUserId(), def.getCreator().getUserId());
	}
    
	/**
	 * Sample task that does not extend AbstractTask
	 */
	static class BareTask implements Task {
		public static ArrayList outputList = new ArrayList();
		
		public void execute() {
			synchronized (outputList) {
				outputList.add("TEST");	            
            }
		}

        public TaskDefinition getTaskDefinition() {
	        return null;
        }
                                                            
        public void initialize(TaskDefinition definition) {
        }

        public boolean isExecuting() {
        	return false;
        }

        public void shutdown() {}
	}
	
	/**
	 * Task which does not return TaskDefinition in getTaskDefinition should run without throwing exceptions.
	 * 
	 * @throws Exception
	 */
	@Test
	public void shouldNotThrowExceptionWhenTaskDefinitionIsNull() throws Exception {
		SchedulerService schedulerService = Context.getSchedulerService();
		
		TaskDefinition td = new TaskDefinition();
		td.setId(10);
        td.setName("Task");
		td.setStartOnStartup(false);
		td.setTaskClass(BareTask.class.getName());
		td.setStartTime(null);

		schedulerService.scheduleTask(td);
		Thread.sleep(500);
		
		assertTrue(BareTask.outputList.contains("TEST"));
	}

	/**
	 * Task opens a session and stores the execution time.
	 */
	static class SessionTask extends AbstractTask {
		public void execute() {
            try {
                // Do something
                Context.openSession();
                Context.addProxyPrivilege(PrivilegeConstants.MANAGE_IMPLEMENTATION_ID);
                Context.getAdministrationService().getImplementationId();
                actualExecutionTime = System.currentTimeMillis();

            } finally {
                Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_IMPLEMENTATION_ID);
                Context.closeSession();
            }

		}
	}

    public static Long actualExecutionTime;

    /**
     * Check saved last execution time.
     */
    @Test
	public void shouldSaveLastExecutionTime() throws Exception {
        final String NAME = "Session Task";
		SchedulerService service = Context.getSchedulerService();

		TaskDefinition td = new TaskDefinition();
		td.setName(NAME);
		td.setStartOnStartup(false);
		td.setTaskClass(SessionTask.class.getName());
        td.setStartTime(null);
    	service.saveTask(td);

		service.scheduleTask(td);
		Thread.sleep(500);

        td = service.getTaskByName(NAME);
		assertEquals("Last execution time in seconds is wrong", actualExecutionTime.longValue() / 1000, td.getLastExecutionTime().getTime() / 1000, 1);
	}
}<commit_msg>Attempt at fixing unstable scheduler service unit test<commit_after>/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.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://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs.scheduler;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Assert;
import org.junit.Test;
import org.openmrs.api.context.Context;
import org.openmrs.scheduler.tasks.AbstractTask;
import org.openmrs.test.BaseContextSensitiveTest;
import org.openmrs.util.OpenmrsClassLoader;
import org.openmrs.util.PrivilegeConstants;

/**
 * TODO test all methods in ScheduleService
 */
public class SchedulerServiceTest extends BaseContextSensitiveTest {
	
	private static Log log = LogFactory.getLog(SchedulerServiceTest.class);

	@Test
	public void shouldResolveValidTaskClass() throws Exception {
		String className = "org.openmrs.scheduler.tasks.TestTask";
		Class c = OpenmrsClassLoader.getInstance().loadClass(className);
		Object o = c.newInstance();
		if (o instanceof Task)
			assertTrue("Class " + className + " is a valid Task", true);
		else
			fail("Class " + className + " is not a valid Task");
	}
	
	@Test(expected = ClassNotFoundException.class)
	public void shouldNotResolveInvalidClass() throws Exception {
		String className = "org.openmrs.scheduler.tasks.InvalidTask";
		Class c = OpenmrsClassLoader.getInstance().loadClass(className);
		Object o = c.newInstance();
		if (o instanceof Task)
			fail("Class " + className + " is not supposed to be a valid Task");
		else
			assertTrue("Class " + className + " is not a valid Task", true);
	}
	
	private static List<String> outputForConcurrentTasks = new ArrayList<String>();
	
	/**
	 * Longer running class used to demonstrate tasks running concurrently
	 */
	static class ExecutePrintingTask extends AbstractTask {
		
		public void execute() {
			synchronized (outputForConcurrentTasks) {
				outputForConcurrentTasks.add(getTaskDefinition().getProperty("id"));
			}
			try {
				Thread.sleep(Integer.valueOf(getTaskDefinition().getProperty("delay")));
			}
			catch (InterruptedException e) {
				log.error("Error generated", e);
			}
			synchronized (outputForConcurrentTasks) {
				outputForConcurrentTasks.add(getTaskDefinition().getProperty("id"));
			}
		}
	}
	
	/**
	 * Demonstrates concurrent running for tasks
	 * 
	 * <pre>
	 *             |
	 * SampleTask2 |    ----
	 * SampleTask1 |------------
	 *             |_____________ time
	 *              ^   ^   ^   ^
	 * Output:     S-1 S-2 E-2 E-1
	 * </pre>
	 */
	@Test
	public void shouldAllowTwoTasksToRunConcurrently() throws Exception {
		SchedulerService schedulerService = Context.getSchedulerService();
		
		TaskDefinition t1 = new TaskDefinition();
		t1.setId(1);
		t1.setStartOnStartup(false);
		t1.setStartTime(null);
		t1.setTaskClass(ExecutePrintingTask.class.getName());
		t1.setProperty("id", "TASK-1");
		t1.setProperty("delay", "400"); // must be longer than t2's delay
		
		TaskDefinition t2 = new TaskDefinition();
		t2.setId(2);
		t2.setStartOnStartup(false);
		t2.setStartTime(null);
		t2.setTaskClass(ExecutePrintingTask.class.getName());
		t2.setProperty("id", "TASK-2");
		t2.setProperty("delay", "100"); // must be shorter than t1's delay
		
		schedulerService.scheduleTask(t1);
		Thread.sleep(50); // so t2 doesn't start before t1 due to random millisecond offsets
		schedulerService.scheduleTask(t2);
		Thread.sleep(600); // must be longer than t2's delay
		assertEquals(Arrays.asList("TASK-1", "TASK-2", "TASK-2", "TASK-1"), outputForConcurrentTasks);
	}
	
	private static List<String> outputForConcurrentInit = new ArrayList<String>();
	
	/**
	 * Longer init'ing class for concurrent init test
	 */
	static class SimpleTask extends AbstractTask {
		
		public void initialize(TaskDefinition config) {
			synchronized (outputForConcurrentInit) {
				outputForConcurrentInit.add(config.getProperty("id"));
			}
			super.initialize(config);
			try {
				// must be less than delay before printing
				Thread.sleep(Integer.valueOf(config.getProperty("delay")));
			}
			catch (InterruptedException e) {
				log.error("Error generated", e);
			}
			synchronized (outputForConcurrentInit) {
				outputForConcurrentInit.add(config.getProperty("id"));
			}
		}
		
		public void execute() {
		}
	}
	
	/**
	 * Demonstrates concurrent initializing for tasks
	 * 
	 * <pre>
	 *             |
	 * SampleTask4 |    ----
	 * SampleTask3 |------------
	 *             |_____________ time
	 *              ^   ^   ^   ^
	 * Output:     S-3 S-4 E-4 E-3
	 * </pre>
	 */
	@Test
	public void shouldAllowTwoTasksInitMethodsToRunConcurrently() throws Exception {
		SchedulerService schedulerService = Context.getSchedulerService();
		
		TaskDefinition t3 = new TaskDefinition();
		t3.setId(3);
		t3.setStartOnStartup(false);
		t3.setStartTime(null); // so it starts immediately
		t3.setTaskClass(SimpleTask.class.getName());
		t3.setProperty("id", "TASK-3");
		t3.setProperty("delay", "300"); // must be longer than t4's delay
		
		TaskDefinition t4 = new TaskDefinition();
		t4.setId(4);
		t4.setStartOnStartup(false);
		t4.setStartTime(null); // so it starts immediately
		t4.setTaskClass(SimpleTask.class.getName());
		t4.setProperty("id", "TASK-4");
		t4.setProperty("delay", "100");
		
		// both of these tasks start immediately
		schedulerService.scheduleTask(t3); // starts first, ends last
		schedulerService.scheduleTask(t4); // starts last, ends first
		Thread.sleep(500); // must be greater than task3 delay so that it prints out its end
		assertEquals(Arrays.asList("TASK-3", "TASK-4", "TASK-4", "TASK-3"), outputForConcurrentInit);
		
		// cleanup
		schedulerService.shutdownTask(t3);
		schedulerService.shutdownTask(t4);
	}
	
	private static List<String> outputForInitExecSync = new ArrayList<String>();
	
	static class SampleTask5 extends AbstractTask {
		
		public void initialize(TaskDefinition config) {
			synchronized (outputForInitExecSync) {
				outputForInitExecSync.add("INIT-START-5");
			}
			super.initialize(config);
			try {
				Thread.sleep(700);
			}
			catch (InterruptedException e) {
				log.error("Error generated", e);
			}
			synchronized (outputForInitExecSync) {
				outputForInitExecSync.add("INIT-END-5");
			}
		}
		
		public void execute() {
			synchronized (outputForInitExecSync) {
				outputForInitExecSync.add("IN EXECUTE");
			}
		}
	}
	
	/**
	 * Demonstrates that initialization of a task is accomplished before its execution without
	 * interleaving, which is a non-trivial behavior in the presence of a threaded initialization
	 * method (as implemented in TaskThreadedInitializationWrapper)
	 * 
	 * <pre>
	 *             |
	 * SampleTask5 |------------
	 *             |_____________ time
	 *              ^   ^   ^   ^
	 * Output:     IS  IE   S   E
	 * </pre>
	 */
	@Test
	public void shouldNotAllowTaskExecuteToRunBeforeInitializationIsComplete() throws Exception {
		SchedulerService schedulerService = Context.getSchedulerService();
		
		TaskDefinition t5 = new TaskDefinition();
		t5.setId(5);
		t5.setStartOnStartup(false);
		t5.setStartTime(null); // immediate start
		t5.setTaskClass(SampleTask5.class.getName());
		
		schedulerService.scheduleTask(t5);
		Thread.sleep(1200);
		assertEquals(Arrays.asList("INIT-START-5", "INIT-END-5", "IN EXECUTE"), outputForInitExecSync);
	}
	
	@Test
	public void saveTask_shouldSaveTaskToTheDatabase() throws Exception {
		SchedulerService service = Context.getSchedulerService();
		Assert.assertEquals(0, service.getRegisteredTasks().size());
		
		TaskDefinition def = new TaskDefinition();
		final String TASK_NAME = "This is my test! 123459876";
		def.setName(TASK_NAME);
		def.setStartOnStartup(false);
		def.setRepeatInterval(10L);
		def.setTaskClass(ExecutePrintingTask.class.getName());
		service.saveTask(def);
		Assert.assertEquals(1, service.getRegisteredTasks().size());
		
		def = service.getTaskByName(TASK_NAME);
		Assert.assertEquals(Context.getAuthenticatedUser().getUserId(), def.getCreator().getUserId());
	}
    
	/**
	 * Sample task that does not extend AbstractTask
	 */
	static class BareTask implements Task {
		public static ArrayList outputList = new ArrayList();
		
		public void execute() {
			synchronized (outputList) {
				outputList.add("TEST");	            
            }
		}

        public TaskDefinition getTaskDefinition() {
	        return null;
        }
                                                            
        public void initialize(TaskDefinition definition) {
        }

        public boolean isExecuting() {
        	return false;
        }

        public void shutdown() {}
	}
	
	/**
	 * Task which does not return TaskDefinition in getTaskDefinition should run without throwing exceptions.
	 * 
	 * @throws Exception
	 */
	@Test
	public void shouldNotThrowExceptionWhenTaskDefinitionIsNull() throws Exception {
		SchedulerService schedulerService = Context.getSchedulerService();
		
		TaskDefinition td = new TaskDefinition();
		td.setId(10);
        td.setName("Task");
		td.setStartOnStartup(false);
		td.setTaskClass(BareTask.class.getName());
		td.setStartTime(null);

		schedulerService.scheduleTask(td);
		Thread.sleep(500);
		
		assertTrue(BareTask.outputList.contains("TEST"));
	}

	/**
	 * Task opens a session and stores the execution time.
	 */
	static class SessionTask extends AbstractTask {
		public void execute() {
            try {
                // Do something
                Context.openSession();
                Context.addProxyPrivilege(PrivilegeConstants.MANAGE_IMPLEMENTATION_ID);
                Context.getAdministrationService().getImplementationId();
                actualExecutionTime = System.currentTimeMillis();

            } finally {
                Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_IMPLEMENTATION_ID);
                Context.closeSession();
            }

		}
	}

    public static Long actualExecutionTime;

    /**
     * Check saved last execution time.
     */
    @Test
	public void shouldSaveLastExecutionTime() throws Exception {
        final String NAME = "Session Task";
		SchedulerService service = Context.getSchedulerService();

		TaskDefinition td = new TaskDefinition();
		td.setName(NAME);
		td.setStartOnStartup(false);
		td.setTaskClass(SessionTask.class.getName());
        td.setStartTime(null);
    	service.saveTask(td);

		service.scheduleTask(td);
		// sleep a while until the task has executed, up to 5 times
		for (int x=0; x<5 && actualExecutionTime == null; x++)
			Thread.sleep(200);

        td = service.getTaskByName(NAME);
        Assert.assertNotNull(actualExecutionTime);
		assertEquals("Last execution time in seconds is wrong", actualExecutionTime.longValue() / 1000, td.getLastExecutionTime().getTime() / 1000, 1);
	}
}
{'fraction_non_alphanumeric': 0.11483911000797802, 'fraction_numerical': 0.012720503501462636, 'mean_word_length': 3.4776741416947807, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2892103', 'n_tokens_mistral': 7323, 'n_tokens_neox': 6501, 'n_words': 1555}