From b37eda2cdf209b892eec6be7f48d63449802c906 Mon Sep 17 00:00:00 2001 From: srosse <stephane.rosse@frentix.com> Date: Wed, 27 Jan 2021 15:43:26 +0100 Subject: [PATCH] no-jira: unit test to simulate task assign under heavy load --- .../java/org/olat/core/util/FileUtils.java | 2 +- .../nodes/gta/manager/GTAManagerTest.java | 170 +++++++++++++++++- 2 files changed, 170 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/olat/core/util/FileUtils.java b/src/main/java/org/olat/core/util/FileUtils.java index 3923a777b3b..c2f8cffff3a 100644 --- a/src/main/java/org/olat/core/util/FileUtils.java +++ b/src/main/java/org/olat/core/util/FileUtils.java @@ -488,7 +488,7 @@ public class FileUtils { deleteFile(sourceFile); } } catch (IOException e) { - log.error("Could not copy file::" + sourceFile.getAbsolutePath() + " to file::" + targetFile.getAbsolutePath(), e); + log.error("Could not copy file::{} to file::{}", sourceFile.getAbsolutePath(), targetFile.getAbsolutePath(), e); return false; } return true; diff --git a/src/test/java/org/olat/course/nodes/gta/manager/GTAManagerTest.java b/src/test/java/org/olat/course/nodes/gta/manager/GTAManagerTest.java index 989b5d22d85..7e76015d3ca 100644 --- a/src/test/java/org/olat/course/nodes/gta/manager/GTAManagerTest.java +++ b/src/test/java/org/olat/course/nodes/gta/manager/GTAManagerTest.java @@ -19,21 +19,29 @@ */ package org.olat.course.nodes.gta.manager; +import static org.assertj.core.api.Assertions.assertThat; + import java.io.File; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.logging.log4j.Logger; import org.junit.Assert; import org.junit.Test; import org.olat.basesecurity.GroupRoles; import org.olat.core.commons.persistence.DB; +import org.olat.core.commons.persistence.DBFactory; import org.olat.core.id.Identity; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; +import org.olat.core.util.FileUtils; import org.olat.core.util.nodes.INode; import org.olat.course.CourseFactory; import org.olat.course.ICourse; @@ -41,12 +49,14 @@ import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.GTACourseNode; import org.olat.course.nodes.gta.AssignmentResponse; import org.olat.course.nodes.gta.AssignmentResponse.Status; +import org.olat.course.nodes.gta.GTAManager; import org.olat.course.nodes.gta.GTAType; import org.olat.course.nodes.gta.Task; import org.olat.course.nodes.gta.TaskList; import org.olat.course.nodes.gta.TaskProcess; import org.olat.course.nodes.gta.TaskRevisionDate; import org.olat.course.nodes.gta.model.TaskListImpl; +import org.olat.course.run.environment.CourseEnvironment; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupService; import org.olat.group.manager.BusinessGroupDAO; @@ -914,6 +924,164 @@ public class GTAManagerTest extends OlatTestCase { Assert.assertEquals(TaskProcess.correction, loadedTaskRevision.getTaskStatus()); } + @Test + public void assignTaskAutomatically() { + //prepare + Identity participant = JunitTestHelper.createAndPersistIdentityAsRndUser("gta-user-1"); + RepositoryEntry re = deployGTACourse(); + GTACourseNode node = getGTACourseNode(re); + node.getModuleConfiguration().setStringValue(GTACourseNode.GTASK_TYPE, GTAType.individual.name()); + node.getModuleConfiguration().setStringValue(GTACourseNode.GTASK_SAMPLING, GTACourseNode.GTASK_SAMPLING_REUSE); + TaskList tasks = gtaManager.createIfNotExists(re, node); + + CourseEnvironment courseEnv = CourseFactory.loadCourse(re).getCourseEnvironment(); + File tasksDirectory = gtaManager.getTasksDirectory(courseEnv, node); + FileUtils.deleteDirsAndFiles(tasksDirectory, true, false); + copyTestFile(tasksDirectory, "File00O.pdf"); + copyTestFile(tasksDirectory, "File0O0.pdf"); + copyTestFile(tasksDirectory, "FileO00.pdf"); + dbInstance.commit(); + + //select + AssignmentResponse response = gtaManager.assignTaskAutomatically(tasks, participant, courseEnv, node); + dbInstance.commitAndCloseSession(); + //check + Assert.assertNotNull(response); + Assert.assertNotNull(response.getTask()); + Assert.assertEquals(AssignmentResponse.Status.ok, response.getStatus()); + + Task task = response.getTask(); + Assert.assertNotNull(task.getKey()); + Assert.assertNull(task.getBusinessGroup()); + Assert.assertNotNull(task.getCreationDate()); + Assert.assertNotNull(task.getLastModified()); + Assert.assertEquals(tasks, task.getTaskList()); + Assert.assertEquals(participant, task.getIdentity()); + assertThat(task.getTaskName()) + .isIn("File00O.pdf", "File0O0.pdf", "FileO00.pdf"); + } + + @Test + public void assignTaskAutomaticallyHighload() { + RepositoryEntry re = deployGTACourse(); + GTACourseNode node = getGTACourseNode(re); + node.getModuleConfiguration().setStringValue(GTACourseNode.GTASK_TYPE, GTAType.individual.name()); + node.getModuleConfiguration().setStringValue(GTACourseNode.GTASK_SAMPLING, GTACourseNode.GTASK_SAMPLING_REUSE); + TaskList tasks = gtaManager.createIfNotExists(re, node); + + CourseEnvironment courseEnv = CourseFactory.loadCourse(re).getCourseEnvironment(); + File tasksDirectory = gtaManager.getTasksDirectory(courseEnv, node); + FileUtils.deleteDirsAndFiles(tasksDirectory, true, false); + copyTestFile(tasksDirectory, "File00O.pdf"); + copyTestFile(tasksDirectory, "File0O0.pdf"); + copyTestFile(tasksDirectory, "FileO00.pdf"); + copyTestFile(tasksDirectory, "FileOO0.pdf"); + copyTestFile(tasksDirectory, "FileO0O.pdf"); + dbInstance.commit(); + + final int numThreads = 25; + final int batchSize = 10; + final int numOfPersons = batchSize * numThreads; + List<Identity> participants = new ArrayList<>(numOfPersons); + for(int i=0; i<numOfPersons; i++) { + Identity participant = JunitTestHelper.createAndPersistIdentityAsRndUser("gta-user-high-load-" + i); + participants.add(participant); + } + + CountDownLatch latch = new CountDownLatch(numThreads); + AssignThread[] threads = new AssignThread[numThreads]; + for(int i=0; i<threads.length;i++) { + List<Identity> batch = participants.subList(i * batchSize, (i + 1) * batchSize); + threads[i] = new AssignThread(batch, tasks, node, courseEnv, gtaManager, latch); + } + + for(int i=0; i<threads.length;i++) { + threads[i].start(); + } + + try { + latch.await(60, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Assert.fail("Takes too long (more than 120sec)"); + } + + int countErrors = 0; + for(int i=0; i<threads.length;i++) { + countErrors += threads[i].getErrors(); + } + Assert.assertEquals(0, countErrors); + + List<Task> taskList = gtaManager.getTasks(tasks, node); + Map<String, AtomicInteger> maps = Map.of("File00O.pdf", new AtomicInteger(0), "File0O0.pdf", new AtomicInteger(0), "FileO00.pdf", new AtomicInteger(0), + "FileOO0.pdf", new AtomicInteger(0), "FileO0O.pdf", new AtomicInteger(0)); + for(Task task:taskList) { + maps.get(task.getTaskName()).incrementAndGet(); + } + + Assert.assertEquals(5, maps.size()); + for(AtomicInteger count:maps.values()) { + log.info("Counter: {}", count.get()); + Assert.assertEquals(50, count.get()); + } + } + + private static class AssignThread extends Thread { + + private final TaskList tasks; + private final GTACourseNode node; + private final GTAManager gtaManager; + private final List<Identity> batch; + private final CourseEnvironment courseEnv; + + private int errors; + private final CountDownLatch latch; + + public AssignThread(List<Identity> batch, TaskList tasks, GTACourseNode node, + CourseEnvironment courseEnv, GTAManager gtaManager, CountDownLatch latch) { + this.batch = new ArrayList<>(batch); + this.latch = latch; + this.tasks = tasks; + this.node = node; + this.courseEnv = courseEnv; + this.gtaManager = gtaManager; + } + + public int getErrors() { + return errors; + } + + @Override + public void run() { + try { + for(Identity participant:batch) { + AssignmentResponse response = gtaManager.assignTaskAutomatically(tasks, participant, courseEnv, node); + if(response == null || response.getTask() == null || response.getStatus() != Status.ok) { + errors++; + } + DBFactory.getInstance().commitAndCloseSession(); + log.info("Assigned: {} {}", response.getStatus(), participant); + } + } catch (Exception e) { + log.error("", e); + errors++; + } finally { + DBFactory.getInstance().commitAndCloseSession(); + latch.countDown(); + } + } + } + + private void copyTestFile(File dir, String name) { + try { + URL url = JunitTestHelper.class.getResource("file_resources/task_1_a.txt"); + File courseFile = new File(url.toURI()); + File targetFile = new File(dir, name); + FileUtils.copyFileToFile(courseFile, targetFile, false); + } catch (URISyntaxException e) { + log.error("", e); + } + } + @Test public void roundRobin() { String[] slots = new String[]{ "A", "B", "C" }; -- GitLab