ProjectManager is now partially unit tested.
git-svn-id: http://google-refine.googlecode.com/svn/trunk@1015 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
parent
f690c55fab
commit
0d7b3b0e9c
@ -6,6 +6,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@ -19,7 +20,11 @@ import com.metaweb.gridworks.model.Project;
|
||||
import com.metaweb.gridworks.preference.PreferenceStore;
|
||||
import com.metaweb.gridworks.preference.TopList;
|
||||
|
||||
|
||||
/**
|
||||
* ProjectManager is responsible for loading and saving the workspace and projects.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public abstract class ProjectManager {
|
||||
// last n expressions used across all projects
|
||||
static protected final int s_expressionHistoryMax = 100;
|
||||
@ -49,6 +54,14 @@ public abstract class ProjectManager {
|
||||
|
||||
static public ProjectManager singleton;
|
||||
|
||||
protected ProjectManager(){
|
||||
_projectsMetadata = new HashMap<Long, ProjectMetadata>();
|
||||
_preferenceStore = new PreferenceStore();
|
||||
_projects = new HashMap<Long, Project>();
|
||||
|
||||
preparePreferenceStore(_preferenceStore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the project in the memory of the current session
|
||||
* @param project
|
||||
@ -102,26 +115,28 @@ public abstract class ProjectManager {
|
||||
*/
|
||||
public void ensureProjectSaved(long id) {
|
||||
synchronized(this){
|
||||
ProjectMetadata metadata = _projectsMetadata.get(id);
|
||||
ProjectMetadata metadata = this.getProjectMetadata(id);
|
||||
if (metadata != null) {
|
||||
try {
|
||||
saveMetadata(metadata, id);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}//FIXME what should be the behaviour if metadata is null? i.e. not found
|
||||
|
||||
Project project = _projects.get(id);
|
||||
if (project != null && metadata.getModified().after(project.lastSave)) {
|
||||
Project project = getProject(id);
|
||||
if (project != null && metadata != null && metadata.getModified().after(project.getLastSave())) {
|
||||
try {
|
||||
saveProject(project);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}//FIXME what should be the behaviour if project is null? i.e. not found or loaded.
|
||||
//FIXME what should happen if the metadata is found, but not the project? or vice versa?
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Save project metadata to the data store
|
||||
* @param metadata
|
||||
@ -138,7 +153,7 @@ public abstract class ProjectManager {
|
||||
|
||||
/**
|
||||
* Save workspace and all projects to data store
|
||||
* @param b
|
||||
* @param allModified
|
||||
*/
|
||||
public void save(boolean allModified) {
|
||||
if (allModified || _busy == 0) {
|
||||
@ -175,23 +190,23 @@ public abstract class ProjectManager {
|
||||
*/
|
||||
protected void saveProjects(boolean allModified) {
|
||||
List<SaveRecord> records = new ArrayList<SaveRecord>();
|
||||
Date now = new Date();
|
||||
Date startTimeOfSave = new Date();
|
||||
|
||||
synchronized (this) {
|
||||
for (long id : _projectsMetadata.keySet()) {
|
||||
ProjectMetadata metadata = _projectsMetadata.get(id);
|
||||
Project project = _projects.get(id);
|
||||
ProjectMetadata metadata = getProjectMetadata(id);
|
||||
Project project = getProject(id);
|
||||
|
||||
if (project != null) {
|
||||
boolean hasUnsavedChanges =
|
||||
metadata.getModified().getTime() > project.lastSave.getTime();
|
||||
metadata.getModified().getTime() > project.getLastSave().getTime();
|
||||
|
||||
if (hasUnsavedChanges) {
|
||||
long msecsOverdue = now.getTime() - project.lastSave.getTime();
|
||||
long msecsOverdue = startTimeOfSave.getTime() - project.getLastSave().getTime();
|
||||
|
||||
records.add(new SaveRecord(project, msecsOverdue));
|
||||
|
||||
} else if (now.getTime() - project.lastSave.getTime() > s_projectFlushDelay) {
|
||||
} else if (startTimeOfSave.getTime() - project.getLastSave().getTime() > s_projectFlushDelay) {
|
||||
/*
|
||||
* It's been a while since the project was last saved and it hasn't been
|
||||
* modified. We can safely remove it from the cache to save some memory.
|
||||
@ -222,7 +237,7 @@ public abstract class ProjectManager {
|
||||
|
||||
for (int i = 0;
|
||||
i < records.size() &&
|
||||
(allModified || (new Date().getTime() - now.getTime() < s_quickSaveTimeout));
|
||||
(allModified || (new Date().getTime() - startTimeOfSave.getTime() < s_quickSaveTimeout));
|
||||
i++) {
|
||||
|
||||
try {
|
||||
|
@ -8,7 +8,6 @@ import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
@ -27,14 +26,13 @@ import com.metaweb.gridworks.ProjectManager;
|
||||
import com.metaweb.gridworks.ProjectMetadata;
|
||||
import com.metaweb.gridworks.history.HistoryEntryManager;
|
||||
import com.metaweb.gridworks.model.Project;
|
||||
import com.metaweb.gridworks.preference.PreferenceStore;
|
||||
import com.metaweb.gridworks.preference.TopList;
|
||||
|
||||
public class FileProjectManager extends ProjectManager {
|
||||
|
||||
protected File _workspaceDir;
|
||||
|
||||
final static Logger logger = LoggerFactory.getLogger("file_project_manager");
|
||||
final static Logger logger = LoggerFactory.getLogger("FileProjectManager");
|
||||
|
||||
static public synchronized void initialize(File dir) {
|
||||
if (singleton == null) {
|
||||
@ -45,15 +43,10 @@ public class FileProjectManager extends ProjectManager {
|
||||
}
|
||||
|
||||
protected FileProjectManager(File dir) {
|
||||
super();
|
||||
_workspaceDir = dir;
|
||||
_workspaceDir.mkdirs();
|
||||
|
||||
_projectsMetadata = new HashMap<Long, ProjectMetadata>();
|
||||
_preferenceStore = new PreferenceStore();
|
||||
_projects = new HashMap<Long, Project>();
|
||||
|
||||
preparePreferenceStore(_preferenceStore);
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.util.Date;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
@ -47,7 +46,7 @@ public class ProjectUtilities {
|
||||
oldFile.delete();
|
||||
}
|
||||
|
||||
project.lastSave = new Date();
|
||||
project.setLastSave();
|
||||
|
||||
logger.info("Saved project '{}'",id);
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ public class Project {
|
||||
|
||||
final public History history;
|
||||
transient public ProcessManager processManager = new ProcessManager();
|
||||
transient public Date lastSave = new Date();
|
||||
transient private Date _lastSave = new Date();
|
||||
|
||||
final static Logger logger = LoggerFactory.getLogger("project");
|
||||
|
||||
@ -49,6 +49,16 @@ public class Project {
|
||||
this.history = new History(this);
|
||||
}
|
||||
|
||||
public Date getLastSave(){
|
||||
return this._lastSave;
|
||||
}
|
||||
/**
|
||||
* Sets the lastSave time to now
|
||||
*/
|
||||
public void setLastSave(){
|
||||
this._lastSave = new Date();
|
||||
}
|
||||
|
||||
public ProjectMetadata getMetadata() {
|
||||
return ProjectManager.singleton.getProjectMetadata(id);
|
||||
}
|
||||
@ -132,7 +142,7 @@ public class Project {
|
||||
|
||||
public void update() {
|
||||
columnModel.update();
|
||||
recordModel.update(this);
|
||||
recordModel.update(this);
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,79 @@
|
||||
package com.metaweb.gridworks.tests;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.tools.tar.TarOutputStream;
|
||||
|
||||
import com.metaweb.gridworks.ProjectManager;
|
||||
import com.metaweb.gridworks.ProjectMetadata;
|
||||
import com.metaweb.gridworks.history.HistoryEntryManager;
|
||||
import com.metaweb.gridworks.model.Project;
|
||||
|
||||
/**
|
||||
* Stub used to make protected methods public for testing
|
||||
*
|
||||
*/
|
||||
public class ProjectManagerStub extends ProjectManager {
|
||||
|
||||
public ProjectManagerStub(){
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProject(long projectID) {
|
||||
// empty
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportProject(long projectId, TarOutputStream tos) throws IOException {
|
||||
// empty
|
||||
}
|
||||
|
||||
@Override
|
||||
public HistoryEntryManager getHistoryEntryManager() {
|
||||
// empty
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importProject(long projectID, InputStream inputStream, boolean gziped) throws IOException {
|
||||
// empty
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Project loadProject(long id) {
|
||||
// empty
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadProjectMetadata(long projectID) {
|
||||
// empty
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveMetadata(ProjectMetadata metadata, long projectId) throws Exception {
|
||||
// empty
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveProject(Project project) {
|
||||
// empty
|
||||
}
|
||||
|
||||
//Overridden to make public for testing
|
||||
@Override
|
||||
public void saveProjects(boolean allModified){
|
||||
super.saveProjects(allModified);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveWorkspace() {
|
||||
// empty
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,212 @@
|
||||
package com.metaweb.gridworks.tests;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import org.mockito.Mockito;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.AfterMethod;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.metaweb.gridworks.ProjectMetadata;
|
||||
import com.metaweb.gridworks.model.Project;
|
||||
import com.metaweb.gridworks.tests.model.ProjectStub;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
||||
public class ProjectManagerTests extends GridworksTest {
|
||||
ProjectManagerStub pm;
|
||||
ProjectManagerStub SUT;
|
||||
Project project;
|
||||
ProjectMetadata metadata;
|
||||
|
||||
@BeforeTest
|
||||
public void init() {
|
||||
logger = LoggerFactory.getLogger(this.getClass());
|
||||
}
|
||||
|
||||
@BeforeMethod
|
||||
public void SetUp(){
|
||||
pm = new ProjectManagerStub();
|
||||
SUT = spy(pm);
|
||||
project = mock(Project.class);
|
||||
metadata = mock(ProjectMetadata.class);
|
||||
}
|
||||
|
||||
@AfterMethod
|
||||
public void TearDown(){
|
||||
metadata = null;
|
||||
project = null;
|
||||
SUT = null;
|
||||
pm = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canRegisterProject(){
|
||||
|
||||
SUT.registerProject(project, metadata);
|
||||
|
||||
AssertProjectRegistered();
|
||||
|
||||
verifyNoMoreInteractions(project);
|
||||
verifyNoMoreInteractions(metadata);
|
||||
}
|
||||
|
||||
//TODO test registerProject in race condition
|
||||
|
||||
@Test
|
||||
public void canEnsureProjectSave(){
|
||||
whenGetSaveTimes(project, metadata);
|
||||
registerProject();
|
||||
|
||||
//run test
|
||||
SUT.ensureProjectSaved(project.id);
|
||||
|
||||
//assert and verify
|
||||
AssertProjectRegistered();
|
||||
try {
|
||||
verify(SUT, times(1)).saveMetadata(metadata, project.id);
|
||||
} catch (Exception e) {
|
||||
Assert.fail();
|
||||
}
|
||||
this.verifySaveTimeCompared(1);
|
||||
verify(SUT, times(1)).saveProject(project);
|
||||
|
||||
//ensure end
|
||||
verifyNoMoreInteractions(project);
|
||||
verifyNoMoreInteractions(metadata);
|
||||
}
|
||||
|
||||
//TODO test ensureProjectSave in race condition
|
||||
|
||||
@Test
|
||||
public void canSaveAllModified(){
|
||||
whenGetSaveTimes(project, metadata); //5 minute difference
|
||||
registerProject(project, metadata);
|
||||
|
||||
//add a second project to the cache
|
||||
Project project2 = spy(new ProjectStub(2));
|
||||
ProjectMetadata metadata2 = mock(ProjectMetadata.class);
|
||||
whenGetSaveTimes(project2, metadata2, 10); //not modified since the last save but within 30 seconds flush limit
|
||||
registerProject(project2, metadata2);
|
||||
|
||||
//check that the two projects are not the same
|
||||
Assert.assertFalse(project.id == project2.id);
|
||||
|
||||
SUT.save(true);
|
||||
|
||||
verifySaved(project, metadata);
|
||||
|
||||
verifySaved(project2, metadata2);
|
||||
|
||||
verify(SUT, times(1)).saveWorkspace();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canFlushFromCache(){
|
||||
|
||||
whenGetSaveTimes(project, metadata, -10 );//already saved (10 seconds before)
|
||||
registerProject(project, metadata);
|
||||
Assert.assertSame(SUT.getProject(0), project);
|
||||
|
||||
SUT.save(true);
|
||||
|
||||
verify(metadata, times(1)).getModified();
|
||||
verify(project, times(2)).getLastSave();
|
||||
verify(SUT, never()).saveProject(project);
|
||||
Assert.assertEquals(SUT.getProject(0), null);
|
||||
verifyNoMoreInteractions(project);
|
||||
verifyNoMoreInteractions(metadata);
|
||||
|
||||
verify(SUT, times(1)).saveWorkspace();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cannotSaveWhenBusy(){
|
||||
registerProject();
|
||||
SUT.setBusy(true);
|
||||
|
||||
SUT.save(false);
|
||||
|
||||
verify(SUT, never()).saveProjects(Mockito.anyBoolean());
|
||||
verify(SUT, never()).saveWorkspace();
|
||||
verifyNoMoreInteractions(project);
|
||||
verifyNoMoreInteractions(metadata);
|
||||
}
|
||||
|
||||
//TODO test canSaveAllModifiedWithRaceCondition
|
||||
|
||||
@Test
|
||||
public void canSaveSomeModified(){
|
||||
registerProject();
|
||||
whenGetSaveTimes(project, metadata );
|
||||
|
||||
SUT.save(false); //not busy
|
||||
|
||||
verifySaved(project, metadata);
|
||||
verify(SUT, times(1)).saveWorkspace();
|
||||
|
||||
}
|
||||
//TODO test canSaveAllModifiedWithRaceCondition
|
||||
|
||||
//-------------helpers-------------
|
||||
|
||||
protected void registerProject(){
|
||||
this.registerProject(project, metadata);
|
||||
}
|
||||
protected void registerProject(Project proj, ProjectMetadata meta){
|
||||
SUT.registerProject(proj, meta);
|
||||
}
|
||||
|
||||
protected void AssertProjectRegistered(){
|
||||
Assert.assertEquals(SUT.getProject(project.id), project);
|
||||
Assert.assertEquals(SUT.getProjectMetadata(project.id), metadata);
|
||||
}
|
||||
|
||||
protected void whenGetSaveTimes(Project proj, ProjectMetadata meta){
|
||||
whenGetSaveTimes(proj, meta, 5);
|
||||
}
|
||||
protected void whenGetSaveTimes(Project proj, ProjectMetadata meta, int secondsDifference){
|
||||
whenProjectGetLastSave(proj);
|
||||
whenMetadataGetModified(meta, secondsDifference);
|
||||
}
|
||||
|
||||
protected void whenProjectGetLastSave(Project proj){
|
||||
Date projectLastSaveDate = new GregorianCalendar(1970,01,02,00,30,00).getTime();
|
||||
when(proj.getLastSave()).thenReturn(projectLastSaveDate);
|
||||
}
|
||||
|
||||
protected void whenMetadataGetModified(ProjectMetadata meta){
|
||||
whenMetadataGetModified(meta, 5*60);
|
||||
}
|
||||
protected void whenMetadataGetModified(ProjectMetadata meta, int secondsDifference){
|
||||
Date metadataModifiedDate = new GregorianCalendar(1970,01,02,00, 30, secondsDifference).getTime();
|
||||
when(meta.getModified()).thenReturn(metadataModifiedDate);
|
||||
}
|
||||
|
||||
protected void verifySaveTimeCompared(int times){
|
||||
verifySaveTimeCompared(project, metadata, times);
|
||||
}
|
||||
protected void verifySaveTimeCompared(Project project, ProjectMetadata metadata, int times){
|
||||
verify(metadata, times(times)).getModified();
|
||||
verify(project, times(times)).getLastSave();
|
||||
}
|
||||
|
||||
protected void verifySaved(Project proj, ProjectMetadata meta){
|
||||
verify(meta, times(1)).getModified();
|
||||
verify(proj, times(2)).getLastSave();
|
||||
verify(SUT, times(1)).saveProject(proj);
|
||||
|
||||
verifyNoMoreInteractions(proj);
|
||||
verifyNoMoreInteractions(meta);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.metaweb.gridworks.tests.model;
|
||||
|
||||
import com.metaweb.gridworks.model.Project;
|
||||
|
||||
|
||||
public class ProjectStub extends Project {
|
||||
public ProjectStub(long id){
|
||||
super(id);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user