/*
* Copyright 2003-2015 JetBrains s.r.o.
*
* Licensed 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 jetbrains.mps.ide.depanalyzer;
import jetbrains.mps.project.DevKit;
import jetbrains.mps.project.Solution;
import jetbrains.mps.smodel.Language;
import jetbrains.mps.testbench.ModuleMpsTest;
import jetbrains.mps.testbench.WriteAction;
import jetbrains.mps.tool.environment.EnvironmentConfig;
import jetbrains.mps.tool.environment.MpsEnvironment;
import org.jetbrains.mps.openapi.module.SModule;
import org.jetbrains.mps.openapi.module.SModuleReference;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import static junit.framework.Assert.assertEquals;
public class ModuleDependenciesTest extends ModuleMpsTest {
@Rule
public WriteAction wa = new WriteAction(); // FIXME shall pass proper ModelAccess in there
@BeforeClass
public static void setUp() {
MpsEnvironment.getOrCreate(EnvironmentConfig.defaultConfig());
}
private List<DepLink> findPaths(DepLink root, SModule target) {
final SModuleReference targetRef = target.getModuleReference();
ArrayList<DepLink> rv = new ArrayList<DepLink>();
for(DepLink dl : root.allDependencies()) {
if (targetRef.equals(dl.module)) {
rv.add(dl);
}
}
return rv;
}
private void testDependency(SModule source, SModule target, boolean showRuntime, int numPaths) {
final List<DepLink> paths = findPaths(new DependencyUtil(getTestRepository()).trackRuntime(showRuntime).build(source), target);
for (Iterator<DepLink> it = paths.iterator(); it.hasNext();) {
DepLink e = it.next();
if (!e.role.isDependency()) {
it.remove();
}
}
assertEquals(numPaths, paths.size());
}
private void testUsedLanguage(DepLink depRoot, Language target, int numPaths) {
final List<DepLink> paths = findPaths(depRoot, target);
for (Iterator<DepLink> it = paths.iterator(); it.hasNext();) {
DepLink e = it.next();
if (!e.role.isUsedLanguage()) {
it.remove();
}
}
assertEquals(numPaths, paths.size());
}
@Test
public void testDependencies() {
final Solution[] solutions = new Solution[5];
for (int i = 0; i < 5; i++) {
solutions[i] = createSolution();
}
final Language[] languages = new Language[] { createLanguage(), createLanguage() };
/*
s[0]---->s[1]--reexport-->s[2]----->s[4]------>l[0]--extends-->l[1]
| |
|---reexport-----|----reexport-->s[3]
*/
solutions[0].addDependency(solutions[1].getModuleReference(), false);
solutions[1].addDependency(solutions[2].getModuleReference(), true);
solutions[2].addDependency(solutions[3].getModuleReference(), true);
solutions[1].addDependency(solutions[3].getModuleReference(), true);
solutions[2].addDependency(solutions[4].getModuleReference(), false);
solutions[4].addDependency(languages[0].getModuleReference(), false);
languages[0].addExtendedLanguage(languages[1].getModuleReference());
testDependency(solutions[0], solutions[1], false, 1); //simple
testDependency(solutions[0], solutions[2], false, 1); //transitive
testDependency(solutions[0], solutions[3], false, 2); //two paths
testDependency(solutions[4], languages[0], false, 1); // simple dependency on language module
testDependency(languages[0], languages[1], false, 1); // dependency on extended language
testDependency(solutions[4], languages[1], false, 1); // re-export dependency on extended language
testDependency(solutions[0], solutions[4], false, 0); //not reexport
testDependency(solutions[0], solutions[4], true, 1); //runtime dependency goes through even without re-export
}
@Test
public void testRuntimeDependencies() {
final Solution[] solutions = new Solution[4];
final Language[] languages = new Language[4];
for (int i = 0; i < 4; i++) {
solutions[i] = createSolution();
languages[i] = createLanguage();
}
addUsedLanguage(solutions[0], languages[0]);
languages[0].getModuleDescriptor().getRuntimeModules().add(solutions[1].getModuleReference());
languages[0].addExtendedLanguage(languages[1].getModuleReference());
languages[1].getModuleDescriptor().getRuntimeModules().add(solutions[2].getModuleReference());
addUsedLanguage(languages[0], languages[2]);
languages[2].getModuleDescriptor().getRuntimeModules().add(solutions[3].getModuleReference());
/*
s[0]---uses--->l[0]----runtime----->s[1]
| |
| extends
| |
| l[1]----runtime----->s[2]
uses
|
l[2]---runtime-->s[3]
*/
testDependency(solutions[0], solutions[1], false, 0); //runtime dependencies are off
testDependency(solutions[0], solutions[1], true, 1); //runtime dependencies are on
testDependency(solutions[0], solutions[2], true, 1); //runtime dependencies can pass through extended language
testDependency(solutions[0], solutions[3], true, 0); //runtime dependencies can not pass through used language
}
@Test
public void testLanguageAndDevkitRuntimeDependencies() {
// check that solutions bring runtime of their languages as runtime dependencies
final Solution[] solutions = new Solution[8];
final Language[] languages = new Language[4];
final DevKit[] devkits = new DevKit[2];
for (int i = 0; i < solutions.length; i++) solutions[i] = createSolution();
for (int i = 0; i < languages.length; i++) languages[i] = createLanguage();
for (int i = 0; i < devkits.length; i++) devkits[i] = createDevKit();
solutions[0].addDependency(solutions[4].getModuleReference(), false);
addUsedLanguage(solutions[4], languages[0]);
languages[0].getModuleDescriptor().getRuntimeModules().add(solutions[1].getModuleReference());
languages[0].addExtendedLanguage(languages[1].getModuleReference());
languages[1].getModuleDescriptor().getRuntimeModules().add(solutions[2].getModuleReference());
addUsedLanguage(languages[0], languages[2]);
languages[2].getModuleDescriptor().getRuntimeModules().add(solutions[3].getModuleReference());
addUsedDevKit(solutions[4], devkits[0]);
devkits[0].getModuleDescriptor().getExportedLanguages().add(languages[3].getModuleReference());
languages[3].getModuleDescriptor().getRuntimeModules().add(solutions[7].getModuleReference());
devkits[0].getModuleDescriptor().getExtendedDevkits().add(devkits[1].getModuleReference());
devkits[1].getModuleDescriptor().getExportedSolutions().add(solutions[5].getModuleReference());
solutions[5].addDependency(solutions[6].getModuleReference(), false);
/*
s[0]----->s[4]---uses--->l[0]----runtime----->s[1]
| | |
| | extends
dk[1]--ext--dk[0] | |
| | | l[1]----runtime----->s[2]
s[5] l[3] uses
| | |
s[6] +-rt--s[7] l[2]---runtime-->s[3]
*/
testDependency(solutions[0], solutions[1], false, 0); //runtime dependencies are off
testDependency(solutions[0], solutions[1], true, 1); //runtime dependencies are on
testDependency(solutions[0], solutions[1], true, 1); //runtime dependencies
testDependency(solutions[0], solutions[2], true, 1); //runtime dependencies can pass through extended language
testDependency(solutions[0], solutions[3], true, 0); //runtime dependencies can not pass through used language
testDependency(solutions[0], solutions[5], true, 1); //runtime dependencies can pass through devkits
testDependency(solutions[0], solutions[6], true, 1); //runtime dependencies can pass through devkits
testDependency(solutions[0], solutions[7], true, 1); //runtime dependencies can pass through devkits and language
testDependency(solutions[0], languages[3], true, 0); //devkit language is not dependency
}
@Test
public void testUsedLanguages() {
final Language[] languages = new Language[7];
for (int i = 0; i < 7; i++) {
languages[i] = createLanguage();
}
DevKit devKit = createDevKit();
DevKit devKit2 = createDevKit();
/*
l[0]--uses-->l[1]--extends-->l[2]---uses-->l[3]
|
devKit---extends-->devKit2-->l[5]-->l[6]
|
l[4]
*/
addUsedLanguage(languages[0], languages[1]);
languages[1].addExtendedLanguage(languages[2].getModuleReference());
addUsedLanguage(languages[2], languages[3]);
devKit.getModuleDescriptor().getExportedLanguages().add(languages[4].getModuleReference());
devKit.getModuleDescriptor().getExtendedDevkits().add(devKit2.getModuleReference());
devKit2.getModuleDescriptor().getExportedLanguages().add(languages[5].getModuleReference());
addUsedDevKit(languages[1], devKit);
languages[5].addExtendedLanguage(languages[6].getModuleReference());
final DepLink l0 = new DependencyUtil(getTestRepository()).trackRuntime(false).build(languages[0]);
final DepLink l1 = new DependencyUtil(getTestRepository()).trackRuntime(false).build(languages[1]);
testUsedLanguage(l0, languages[1], 1); //simple
testUsedLanguage(l0, languages[2], 1); //extended language is usedLanguage
testUsedLanguage(l0, languages[3], 0);
testUsedLanguage(l1, languages[4], 2); // one as exported from devKit, another as usedLanguage (resolved devkit)
testUsedLanguage(l0, languages[4], 0); //extended lang + devKit
testUsedLanguage(l1, languages[5], 2); // One as exported from extended DevKit, another as usedLanguage (resolved extended devkit)
testUsedLanguage(l1, languages[6], 1); //if one uses a language, it takes it as a whole, with all extended languages
}
@Test
public void testExtendedLanguageAsDependencies() {
final Solution[] solutions = new Solution[2];
final Language[] languages = new Language[2];
for (int i = 0; i < solutions.length; i++) solutions[i] = createSolution();
for (int i = 0; i < languages.length; i++) languages[i] = createLanguage();
solutions[0].addDependency(solutions[1].getModuleReference(), false);
solutions[1].addDependency(languages[0].getModuleReference(), false);
languages[0].addExtendedLanguage(languages[1].getModuleReference());
/*
s[0]------>s[1]------->l[0]----extends---->l[1]
*/
testDependency(languages[0], languages[1], false, 1); // extends is dependency
testDependency(solutions[1], languages[1], false, 1); // extends is re-exported dependency
testDependency(solutions[0], languages[1], false, 0); // extends is re-exported dependency, cannot go through nonexported dependency
testDependency(solutions[0], languages[1], true, 1); // runtime dependencies can go through other dependencies
}
}