/******************************************************************************* * Copyright (c) 2014 Bruno Medeiros and other Contributors. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package mmrnmhrm.core.dub_model; import static melnorme.utilbox.core.Assert.AssertNamespace.assertFail; import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull; import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue; import java.nio.file.Path; import java.util.Collection; import java.util.LinkedList; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.junit.BeforeClass; import dtool.dub.CommonDubTest; import dtool.dub.CommonDubTest.DubBundleChecker; import dtool.dub.DubBundle.DubBundleException; import dtool.dub.DubBundleDescription; import dtool.dub.DubDescribeParserTest; import melnorme.lang.ide.core.LangCore; import melnorme.lang.ide.core.project_model.view.BundleErrorElement; import melnorme.lang.ide.core.project_model.view.DependenciesContainer; import melnorme.lang.ide.core.project_model.view.IBundleModelElement; import melnorme.lang.ide.core.utils.ResourceUtils; import melnorme.lang.tooling.BundlePath; import melnorme.lang.tooling.bundle.BundleInfo; import melnorme.utilbox.concurrency.ITaskAgent; import melnorme.utilbox.concurrency.LatchRunnable; import melnorme.utilbox.core.CommonException; import melnorme.utilbox.misc.CollectionUtil; import melnorme.utilbox.misc.Location; import mmrnmhrm.core.dub_model.DeeBundleModelManager.DeeBundleModel; import mmrnmhrm.tests.CommonDeeWorkspaceTest; abstract class JsHelpers extends CommonDeeWorkspaceTest { public static String jsObject(String... entries) { StringBuilder sb = new StringBuilder(); sb.append("{"); for (String entry : entries) { sb.append(entry); if(!entry.endsWith(",")) { sb.append(","); } } sb.append("\"dummyEndKey\" : null } "); return sb.toString(); } public static String jsEntry(String key, String value) { return "\""+key+"\" : \""+value+"\","; } public static String jsEntryValue(String key, Object value) { return "\""+key+"\" : "+value.toString()+","; } public static String jsFileEnd() { return "\"dummyEndKey\" : null } "; } public static String jsEntry(String key, CharSequence value) { return "\"" + key + "\" : " + jsToString(value) + ","; } private static String jsToString(CharSequence value) { if(value instanceof String) { return "\""+value+"\""; } else { return value.toString(); } } public static StringBuilder jsArray(CharSequence... objs) { StringBuilder sb = new StringBuilder(); sb.append("["); for (CharSequence obj : objs) { sb.append(jsToString(obj)); sb.append(","); } sb.append("]"); return sb; } } /** * Utilities for manipulation of Dub projects */ public abstract class AbstractDeeModelManagerTest extends JsHelpers { protected static final Location ECLIPSE_WORKSPACE_PATH = ResourceUtils.getWorkspaceLocation(); @BeforeClass public static void initDubRepositoriesPath() throws CommonException { DubDescribeParserTest.initDubRepositoriesPath(); DubDescribeParserTest.dubAddPath(ECLIPSE_WORKSPACE_PATH); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.out.println("BaseDubModelManagerTest shutdown hook."); try { DubDescribeParserTest.dubRemovePath(ECLIPSE_WORKSPACE_PATH); } catch(CommonException e) { // Ignore } try { DubDescribeParserTest.cleanupDubRepositoriesPath(); } catch(CommonException e) { // Ignore } } }); _awaitModelUpdates_(); } /* ----------------- ----------------- */ protected static Location loc(IProject project) { return loc(project.getLocation().toFile().toPath()); } protected static BundlePath bpath(IProject project) { return BundlePath.create(project.getLocation().toFile().toPath()); } protected static DeeBundleModelManager getBundleModelManager() { return LangCore.deeBundleModelManager(); } protected static ITaskAgent getModelAgent() { return getBundleModelManager().internal_getModelAgent(); } protected static void _awaitModelUpdates_() { try { getBundleModelManager().syncPendingUpdates(); } catch(InterruptedException e) { assertFail(); } } protected static final DeeBundleModel model = getBundleModelManager().getModel(); protected static DubBundleDescription getExistingDubBundleInfo(IProject project) { return assertNotNull(model.getBundleInfo(project)).getBundleDesc(); } public static DependenciesContainer getDubContainer(IProject project) { BundleInfo bundleInfo = assertNotNull(model.getBundleInfo(project)); return new DependenciesContainer(bundleInfo, project); } protected static LatchRunnable writeDubJsonWithModelLatch(IProject project, String contents) throws CoreException { LatchRunnable latchRunnable = new LatchRunnable(); getModelAgent().submitBasicRunnable(latchRunnable); writeDubJson(project, contents); return latchRunnable; } protected static void writeDubJson(IProject project, String contents) throws CoreException { writeStringToFile(project, "dub.json", contents); } public static Path[] srcFolders(String... elems) { return CommonDubTest.paths(elems); } public void writeDubJsonAndCheckDubModel(String dubJson, IProject project, DubBundleChecker expMainBundle) throws CoreException { LatchRunnable preUpdateLatch = writeDubJsonWithModelLatch(project, dubJson); DubBundleDescription unresolvedBundleDesc = getExistingDubBundleInfo(project); preUpdateLatch.releaseAll(); checkDubModel(unresolvedBundleDesc, project, expMainBundle); } public void checkDubModel(DubBundleDescription unresolvedDubBundle, IProject project, DubBundleChecker expMainBundle) throws CoreException { checkUnresolvedBundle(project, expMainBundle, unresolvedDubBundle); _awaitModelUpdates_(); DubBundleDescription dubBundle = getExistingDubBundleInfo(project); if(unresolvedDubBundle.hasErrors()) { // Check that we did not attempt to call dub describe on a manifest with errors assertTrue(unresolvedDubBundle == dubBundle); } DubBundleException error = dubBundle.getError(); if(error != null) { expMainBundle.checkBundleDescription(unresolvedDubBundle, false); testDubContainerUnresolved(project, expMainBundle, true); IMarker errorMarker = assertNotNull(getDubErrorMarker(project)); assertTrue(errorMarker.getAttribute(IMarker.MESSAGE, "").startsWith(error.getMessage())); assertEquals(errorMarker.getAttribute(IMarker.SEVERITY), IMarker.SEVERITY_ERROR); } else { checkFullyResolvedCode(project, dubBundle, expMainBundle); } } /* ----------------- result checking code ----------------- */ protected IMarker getDubErrorMarker(IProject project) throws CoreException { IMarker[] markers = DeeBundleModelManager.getDubErrorMarkers(project); if(markers.length == 0) return null; assertTrue(markers.length == 1); return markers[0]; } protected void checkUnresolvedBundle(IProject project, DubBundleChecker expMainBundle, DubBundleDescription dubBundleDesc) { expMainBundle.checkBundleDescription(dubBundleDesc, false); testDubContainerUnresolved(project, expMainBundle, !expMainBundle.isResolvedOnlyError()); } protected void checkFullyResolvedCode(IProject project, DubBundleDescription dubBundle, DubBundleChecker expMainBundle) throws CoreException { expMainBundle.checkBundleDescription(dubBundle, true); testDubContainer(project, expMainBundle); // IScriptProject dubProject = DLTKCore.create(project); // checkRawBuildpath(dubProject.getRawBuildpath(), expMainBundle.sourceFolders); // checkResolvedBuildpath(dubProject.getResolvedBuildpath(false), expMainBundle.sourceFolders); IMarker[] dubErrorMarkers = DeeBundleModelManager.getDubErrorMarkers(project); assertTrue(dubErrorMarkers.length == 0); } protected void testDubContainerUnresolved(IProject project, DubBundleChecker expMainBundle, boolean expectedError) { DependenciesContainer dubContainer = getDubContainer(project); LinkedList<IBundleModelElement> depChildren = CollectionUtil.createLinkedList(dubContainer.getChildren()); for (String rawDep : expMainBundle.rawDeps) { checkAndRemoveRawDep(depChildren, rawDep); } if(expectedError) { removeErrorElement(expMainBundle, depChildren); } assertTrue(depChildren.isEmpty()); } protected void checkAndRemoveRawDep(Collection<IBundleModelElement> children, String depName) { assertNotNull(removeChildDep(children, depName)); } protected IBundleModelElement removeChildDep(Collection<IBundleModelElement> children, String depName) { for (IBundleModelElement dubElement : children) { if(dubElement instanceof IBundleModelElement) { IBundleModelElement depElement = (IBundleModelElement) dubElement; if(depElement.getElementName().equals(depName)) { assertTrue(children.remove(dubElement)); return depElement; } } } return null; } protected void testDubContainer(IProject project, DubBundleChecker expMainBundle) { DependenciesContainer dubContainer = getDubContainer(project); assertTrue(dubContainer.getBundleInfo().getBundleDesc().isResolved()); IBundleModelElement[] children = dubContainer.getChildren(); LinkedList<IBundleModelElement> depChildren = CollectionUtil.createLinkedList(children); for (DubBundleChecker dep : expMainBundle.expectedDeps) { checkAndRemoveChildDep(depChildren, dep); } removeErrorElement(expMainBundle, depChildren); assertTrue(depChildren.isEmpty()); } protected void removeErrorElement(DubBundleChecker expMainBundle, LinkedList<IBundleModelElement> depChildren) { if(expMainBundle.errorMsgStart != null) { assertTrue(depChildren.size() > 0); IBundleModelElement removed = depChildren.remove(0); BundleErrorElement dubErrorElement = assertCast(removed, BundleErrorElement.class); assertTrue(dubErrorElement.errorDescription.contains(expMainBundle.errorMsgStart)); } } protected void checkAndRemoveChildDep(Collection<IBundleModelElement> children, DubBundleChecker dep) { String depName = dep.bundleName; IBundleModelElement depElement = removeChildDep(children, depName); assertNotNull(depElement); if(dep.sourceFolders == null) { return; } // TODO: check children } /* ----------------- buildpath checking ----------------- */ // public static void checkRawBuildpath(IBuildpathEntry[] rawBuildpath, Path[] srcFolders) throws ModelException { // HashSet<Path> sourcePaths = hashSet(srcFolders); // // for (IBuildpathEntry bpEntry : rawBuildpath) { // IPath entryPath = bpEntry.getPath(); // // if((bpEntry.getEntryKind() == IBuildpathEntry.BPE_CONTAINER)) { // assertFail(); // continue; // } // // assertTrue(bpEntry.getEntryKind() == IBuildpathEntry.BPE_SOURCE); // assertTrue(bpEntry.isExternal() == false); // IPath folderPath = entryPath.removeFirstSegments(1); // Remove project segment // assertTrue(sourcePaths.remove(folderPath.toFile().toPath())); // continue; // } // // // Ensure we matched every entry // assertTrue(sourcePaths.isEmpty()); // } // // public static void checkResolvedBuildpath(IBuildpathEntry[] buildpath, Path[] srcFolders) throws ModelException { // HashSet<Path> sourcePaths = hashSet(srcFolders); // // LinkedList<IBuildpathEntry> buildpathToVerify = CollectionUtil.createLinkedList(buildpath); // // for (ListIterator<IBuildpathEntry> iter = buildpathToVerify.listIterator(); iter.hasNext(); ) { // IBuildpathEntry bpEntry = iter.next(); // // IPath entryPath = EnvironmentPathUtils.getLocalPath(bpEntry.getPath()); // // if(bpEntry.getEntryKind() == IBuildpathEntry.BPE_LIBRARY) { // String entryPathStr = entryPath.toString(); // assertTrue( // entryPathStr.endsWith("druntime/import") || // entryPathStr.endsWith("phobos") || // entryPathStr.startsWith("#special#builtin")); // iter.remove(); // continue; // } // // assertTrue(bpEntry.getEntryKind() == IBuildpathEntry.BPE_SOURCE); // assertTrue(bpEntry.isExternal() == false); // IPath folderPath = entryPath.removeFirstSegments(1); // Remove project segment // assertTrue(sourcePaths.remove(folderPath.toFile().toPath())); // iter.remove(); // continue; // } // // // Ensure we matched every entry // assertTrue(sourcePaths.isEmpty()); // assertTrue(buildpathToVerify.isEmpty()); // } }