package com.oracle.truffle.js.test.interop;
import static com.oracle.truffle.js.lang.JavaScriptLanguage.ID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.FileSystem;
import org.junit.Test;
import com.oracle.truffle.js.test.JSTest;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.AccessMode;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ESModuleTest {
private static void commonCheck(Value v) {
assertTrue(v.hasArrayElements());
assertTrue(v.getArrayElement(0).isNumber());
assertEquals(121, v.getArrayElement(0).asInt());
assertTrue(v.getArrayElement(1).isNumber());
assertEquals(5, v.getArrayElement(1).asInt());
assertTrue(v.getArrayElement(2).isNumber());
assertEquals(11, v.getArrayElement(2).asInt());
}
private static void copyResourceToFile(String resource, File file) throws IOException {
copyResourceToFile(resource, file, new String[0][0]);
}
private static void copyResourceToFile(String resource, File file, String[][] moduleNameReplacements) throws IOException {
InputStream inputStream = ESModuleTest.class.getResourceAsStream(resource);
try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
String line;
while ((line = br.readLine()) != null) {
for (String[] moduleNameReplacement : moduleNameReplacements) {
line = line.replace("'" + moduleNameReplacement[0] + "'", "'" + moduleNameReplacement[1] + "'");
}
bw.write(line);
bw.write(System.lineSeparator());
}
}
}
private static String stripToLastSlash(String resourceName) {
return resourceName.contains("/") ? resourceName.substring(resourceName.lastIndexOf("/") + 1) : resourceName;
}
private static String[] baseAndExtension(String fileName) {
String fileNameBase = fileName.contains(".") ? fileName.substring(0, fileName.lastIndexOf(".")) : fileName;
String fileNameExtension = fileName.substring(fileNameBase.length());
return new String[]{fileNameBase, fileNameExtension};
}
private static File[] prepareTestFileAndModules(String mainFileResource, String... moduleFileResources) throws IOException {
List<String> moduleNames = new ArrayList<>();
List<File> moduleFiles = new ArrayList<>();
int moduleNamesWithExtensionCount = 0;
for (String moduleFileResource : moduleFileResources) {
String moduleName = stripToLastSlash(moduleFileResource);
String[] moduleNameBaseAndExtension = baseAndExtension(moduleName);
String moduleNameBase = moduleNameBaseAndExtension[0];
String moduleNameExtension = moduleNameBaseAndExtension[1];
if (!moduleNameExtension.isEmpty()) {
moduleNamesWithExtensionCount++;
}
File moduleFile = File.createTempFile(moduleNameBase, moduleNameExtension);
moduleFile.deleteOnExit();
copyResourceToFile(moduleFileResource, moduleFile);
moduleNames.add(moduleName);
moduleFiles.add(moduleFile);
}
String[][] moduleNameReplacements = new String[moduleFiles.size() + moduleNamesWithExtensionCount][];
int replacementIndex = 0;
for (int i = 0; i < moduleFiles.size(); i++) {
String moduleName = moduleNames.get(i);
String moduleTempFileName = moduleFiles.get(i).getName();
moduleNameReplacements[replacementIndex++] = new String[]{moduleName, moduleTempFileName};
if (moduleName.contains(".")) {
String moduleNameBase = baseAndExtension(moduleName)[0];
String moduleTempFileNameBase = baseAndExtension(moduleTempFileName)[0];
moduleNameReplacements[replacementIndex++] = new String[]{moduleNameBase, moduleTempFileNameBase};
}
}
String mainFileName = stripToLastSlash(mainFileResource);
String[] mainFileNameBaseAndExtension = baseAndExtension(mainFileName);
String mainFileNameBase = mainFileNameBaseAndExtension[0];
String mainFileNameExtension = mainFileNameBaseAndExtension[1];
File mainFile = File.createTempFile(mainFileNameBase, mainFileNameExtension);
mainFile.deleteOnExit();
copyResourceToFile(mainFileResource, mainFile, moduleNameReplacements);
List<File> allFilesList = new ArrayList<>();
allFilesList.add(mainFile);
allFilesList.addAll(moduleFiles);
return allFilesList.toArray(new File[0]);
}
private static void deleteFiles(File[] filesArray) {
if (filesArray != null) {
for (File file : filesArray) {
file.delete();
}
}
}
@Test
public void testFunctionExport() throws IOException {
File[] allFilesArray = null;
try (Context context = JSTest.newContextBuilder().allowIO(true).build()) {
allFilesArray = prepareTestFileAndModules("resources/functionexporttest.js", "resources" +
"/functionexportmodule.js");
Source mainSource = Source.newBuilder(ID, allFilesArray[0]).mimeType("application/javascript+module").build();
Value v = context.eval(mainSource);
commonCheck(v);
} finally {
deleteFiles(allFilesArray);
}
}
@Test
public void testFunctionExportNoMimeType() throws IOException {
File[] allFilesArray = null;
try (Context context = JSTest.newContextBuilder().allowIO(true).build()) {
allFilesArray = prepareTestFileAndModules("resources/functionexporttest.js", "resources" +
"/functionexportmodule.js");
String mainFilePath = allFilesArray[0].getAbsolutePath();
String[] mainFileBaseAndExtension = baseAndExtension(mainFilePath);
File mainFileWithMjsExtension = new File(mainFileBaseAndExtension[0] + ".mjs");
allFilesArray[0].renameTo(mainFileWithMjsExtension);
Source mainSource = Source.newBuilder(ID, mainFileWithMjsExtension).build();
Value v = context.eval(mainSource);
commonCheck(v);
} finally {
deleteFiles(allFilesArray);
}
}
@Test
public void testDefaultFunctionExport() throws IOException {
File[] allFilesArray = null;
try (Context context = JSTest.newContextBuilder().allowIO(true).build()) {
allFilesArray = prepareTestFileAndModules("resources/defaultfunctionexporttest.js", "resources/diagmodule" +
".js");
Source mainSource = Source.newBuilder(ID, allFilesArray[0]).mimeType("application/javascript+module").build();
Value v = context.eval(mainSource);
commonCheck(v);
} finally {
deleteFiles(allFilesArray);
}
}
@Test
public void testRenamedExport() throws IOException {
File[] allFilesArray = null;
try (Context context = JSTest.newContextBuilder().allowIO(true).build()) {
allFilesArray = prepareTestFileAndModules("resources/renamedexporttest.js", "resources" +
"/renamedexportmodule.js");
Source mainSource = Source.newBuilder(ID, allFilesArray[0]).mimeType("application/javascript+module").build();
Value v = context.eval(mainSource);
commonCheck(v);
} finally {
deleteFiles(allFilesArray);
}
}
@Test
public void testClassExport() throws IOException {
File[] allFilesArray = null;
try (Context context = JSTest.newContextBuilder().allowIO(true).build()) {
allFilesArray = prepareTestFileAndModules("resources/classexporttest.js", "resources/classexportmodule.js");
Source mainSource = Source.newBuilder(ID, allFilesArray[0]).mimeType("application/javascript+module").build();
Value v = context.eval(mainSource);
commonCheck(v);
} finally {
deleteFiles(allFilesArray);
}
}
@Test
public void testDefaultClassExport() throws IOException {
File[] allFilesArray = null;
try (Context context = JSTest.newContextBuilder().allowIO(true).build()) {
allFilesArray = prepareTestFileAndModules("resources/defaultclassexporttest.js", "resources/mymathmodule" +
".js");
Source mainSource = Source.newBuilder(ID, allFilesArray[0]).mimeType("application/javascript+module").build();
Value v = context.eval(mainSource);
commonCheck(v);
} finally {
deleteFiles(allFilesArray);
}
}
@Test
public void testImportWithCustomFileSystem() throws IOException {
File[] allFilesArray = null;
FileSystem fileSystem = new FileSystem() {
java.nio.file.FileSystem fullIO = FileSystems.getDefault();
@Override
public Path parsePath(URI uri) {
return fullIO.provider().getPath(uri);
}
@Override
public Path parsePath(String path) {
return fullIO.getPath(path);
}
@Override
public void checkAccess(Path path, Set<? extends AccessMode> modes, LinkOption... linkOptions) throws IOException {
if (linkOptions.length > 0) {
throw new UnsupportedOperationException("CheckAccess for this FileSystem is unsupported with non " +
"empty link options.");
}
fullIO.provider().checkAccess(path, modes.toArray(new AccessMode[]{}));
}
@Override
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
fullIO.provider().createDirectory(dir, attrs);
}
@Override
public void delete(Path path) throws IOException {
fullIO.provider().delete(path);
}
@Override
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options,
FileAttribute<?>... attrs) throws IOException {
return fullIO.provider().newByteChannel(path, options, attrs);
}
@Override
public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException {
return fullIO.provider().newDirectoryStream(dir, filter);
}
@Override
public Path toAbsolutePath(Path path) {
return path.toAbsolutePath();
}
@Override
public Path toRealPath(Path path, LinkOption... linkOptions) throws IOException {
Path realPath = path;
if (!Files.exists(path, linkOptions) && !path.getFileName().toString().endsWith(".js")) {
realPath = path.resolveSibling(path.getFileName() + ".js");
}
return realPath.toRealPath(linkOptions);
}
@Override
public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
return fullIO.provider().readAttributes(path, attributes, options);
}
};
try (Context context = JSTest.newContextBuilder().allowIO(true).fileSystem(fileSystem).build()) {
allFilesArray = prepareTestFileAndModules("resources/importwithcustomfilesystemtest.js", "resources" +
"/functionexportmodule.js");
Source mainSource = Source.newBuilder(ID, allFilesArray[0]).mimeType("application/javascript+module").build();
Value v = context.eval(mainSource);
commonCheck(v);
} finally {
deleteFiles(allFilesArray);
}
}
}