/*******************************************************************************
* Copyright (c) 2011 Arapiki Solutions Inc.
* 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:
* "Peter Smith <psmith@arapiki.com>" - initial API and
* implementation and/or initial documentation
*******************************************************************************/
package com.buildml.scanner.legacy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.File;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.buildml.model.IActionMgr;
import com.buildml.model.IActionMgr.OperationType;
import com.buildml.model.IBuildStore;
import com.buildml.model.IFileMgr;
import com.buildml.model.IFileMgr.PathType;
import com.buildml.utils.os.SystemUtils;
/**
* Basic testing that the LegacyBuildScanner can produce a valid
* BuildStore. There are many test cases, split over multiple
* test case files, with this file testing C Functions that
* manipulate directories.
*
* @author "Peter Smith <psmith@arapiki.com>"
*/
public class TestCFuncDir {
/* variables used in many test cases */
private IBuildStore bs = null;
private IActionMgr actionMgr = null;
private IFileMgr fileMgr = null;
private int rootAction;
private int action;
private Integer fileAccesses[], fileReads[], fileWrites[], fileModifies[], fileDeletes[];
/** temporary directory into which test cases can store files */
private File tmpDir;
/*-------------------------------------------------------------------------------------*/
/**
* Called before each test case starts. Creates a temporary directory in which the
* test case can store temporary files.
* @throws Exception
*/
@Before
public void setUp() throws Exception {
tmpDir = SystemUtils.createTempDir();
}
/*-------------------------------------------------------------------------------------*/
/**
* Called after each test case ends. Removes the temporary directory and its content.
* @throws Exception
*/
@After
public void tearDown() throws Exception {
SystemUtils.deleteDirectory(tmpDir);
}
/*-------------------------------------------------------------------------------------*/
/**
* Given the source code of a small C program, compile the program and scan it into a
* BuildStore. We then retrieve the one (and only) action that was registered in the
* BuildStore, along with the lists of files that were accessed (accessed, read, written,
* and deleted).
* @param programCode The body of the small C program to be compiled.
* @param args The command line arguments to pass to the small C program.
* @throws Exception Something went wrong when compiling/running the program.
*/
private void traceOneProgram(String programCode, String args[]) throws Exception {
/* compile, run, and trace the program */
bs = BuildScannersCommonTestUtils.parseLegacyProgram(tmpDir, programCode, args);
/* fetch references to sub objects */
actionMgr = bs.getActionMgr();
fileMgr = bs.getFileMgr();
/* find the root action */
rootAction = actionMgr.getRootAction("root");
/* there should only be one child action */
Integer childActions[] = actionMgr.getChildren(rootAction);
assertEquals(1, childActions.length);
/* this is the action ID of the one action */
action = childActions[0];
/* fetch the file access arrays */
fileAccesses = actionMgr.getFilesAccessed(action, OperationType.OP_UNSPECIFIED);
fileReads = actionMgr.getFilesAccessed(action, OperationType.OP_READ);
fileWrites = actionMgr.getFilesAccessed(action, OperationType.OP_WRITE);
fileModifies = actionMgr.getFilesAccessed(action, OperationType.OP_MODIFIED);
fileDeletes = actionMgr.getFilesAccessed(action, OperationType.OP_DELETE);
}
/*-------------------------------------------------------------------------------------*/
/**
* Test the chdir() C function.
* @throws Exception
*/
@Test
public void testChdir() throws Exception {
/*
* Chdir to a path that exists.
*/
traceOneProgram(
"#include <unistd.h>\n" +
"int main() {" +
" chdir(\"" + tmpDir + "\");" +
" system(\"true\");" +
"}", null);
/* test that the child action ("true") executed in tmpDir */
Integer childActions[] = actionMgr.getChildren(action);
assertEquals(1, childActions.length);
int dirId = (Integer) actionMgr.getSlotValue(childActions[0], IActionMgr.DIRECTORY_SLOT_ID);
assertEquals(tmpDir.toString(), fileMgr.getPathName(dirId));
/*
* Chdir to a path that doesn't exist.
*/
traceOneProgram(
"#include <unistd.h>\n" +
"int main() {" +
" chdir(\"/\");" +
" chdir(\"" + tmpDir + "/invalid\");" +
" system(\"true\");" +
"}", null);
/* test that the child action ("true") executes in /, rather than tmpdir/invalid */
childActions = actionMgr.getChildren(action);
assertEquals(1, childActions.length);
dirId = (Integer) actionMgr.getSlotValue(childActions[0], IActionMgr.DIRECTORY_SLOT_ID);
assertEquals("/", fileMgr.getPathName(dirId));
}
/*-------------------------------------------------------------------------------------*/
/**
* Test the fchdir() C function.
* @throws Exception
*/
@Test
public void testFchdir() throws Exception {
/*
* fchdir to a path that exists.
*/
traceOneProgram(
"#include <fcntl.h>\n" +
"#include <unistd.h>\n" +
"int main() {" +
" chdir(\"/\");" +
" int fd = open(\"" + tmpDir +"\", O_RDONLY);" +
" fchdir(fd);" +
" system(\"true\");" +
"}", null);
/* test that the child action ("true") executed in tmpDir */
Integer childActions[] = actionMgr.getChildren(action);
assertEquals(1, childActions.length);
int dirId = (Integer) actionMgr.getSlotValue(childActions[0], IActionMgr.DIRECTORY_SLOT_ID);
assertEquals(tmpDir.toString(), fileMgr.getPathName(dirId));
/*
* fchdir to a bad file descriptor
*/
traceOneProgram(
"#include <fcntl.h>\n" +
"#include <unistd.h>\n" +
"int main() {" +
" chdir(\"/\");" +
" fchdir(-1);" +
" system(\"true\");" +
"}", null);
/* test that the child action ("true") executes in / */
childActions = actionMgr.getChildren(action);
assertEquals(1, childActions.length);
dirId = (Integer) actionMgr.getSlotValue(childActions[0], IActionMgr.DIRECTORY_SLOT_ID);
assertEquals("/", fileMgr.getPathName(dirId));
}
/*-------------------------------------------------------------------------------------*/
/**
* Test the mkdir() C function.
* @throws Exception
*/
@Test
public void testMkdir() throws Exception {
/*
* Make a valid directory, and check that the top-level action
* is credited with making it.
*/
traceOneProgram(
"#include <unistd.h>\n" +
"int main() {" +
" chdir(\"" + tmpDir + "\");" +
" mkdir(\"newDir\", 0755);" +
" return 0;" +
"}", null);
assertEquals(1, fileAccesses.length);
assertEquals(1, fileWrites.length);
String dirName = fileMgr.getPathName(fileAccesses[0]);
assertEquals(tmpDir + "/newDir", dirName);
assertEquals(PathType.TYPE_DIR, fileMgr.getPathType(fileAccesses[0]));
/*
* Fail to make a directory - should not be logged.
*/
traceOneProgram(
"#include <unistd.h>\n" +
"int main() {" +
" mkdir(\"/invalid/dir\", 0755);" +
" return 0;" +
"}", null);
assertEquals(0, fileAccesses.length);
}
/*-------------------------------------------------------------------------------------*/
/**
* Test the mkdirat() C function.
* @throws Exception
*/
@Test
public void testMkdirat() throws Exception {
/*
* Make a valid directory, and check that the top-level action
* is credited with making it.
*/
traceOneProgram(
"#include <fcntl.h>\n" +
"#include <unistd.h>\n" +
"int main() {" +
" int dirfd = open(\"" + tmpDir + "\", O_RDONLY);" +
" mkdirat(dirfd, \"anotherDir\", 0755);" +
" return 0;" +
"}", null);
assertEquals(2, fileAccesses.length);
assertEquals(1, fileReads.length);
assertEquals(1, fileWrites.length);
String dirName = fileMgr.getPathName(fileWrites[0]);
assertEquals(tmpDir + "/anotherDir", dirName);
assertEquals(PathType.TYPE_DIR, fileMgr.getPathType(fileWrites[0]));
/*
* Failing to make a directory will result in no accesses.
*/
traceOneProgram(
"#include <fcntl.h>\n" +
"#include <unistd.h>\n" +
"int main() {" +
" int dirfd = open(\"" + tmpDir + "\", O_RDONLY);" +
" mkdirat(dirfd, \"/invalid/dir\", 0755);" +
" return 0;" +
"}", null);
assertEquals(1, fileAccesses.length);
assertEquals(1, fileReads.length);
assertEquals(0, fileWrites.length);
}
/*-------------------------------------------------------------------------------------*/
/**
* Test the rmdir() C function.
* @throws Exception
*/
@Test
public void testRmdir() throws Exception {
/*
* Delete a valid directory, and check that the top-level action
* is credited with removing it.
*/
assertTrue(new File(tmpDir, "newDir").mkdirs());
traceOneProgram(
"#include <unistd.h>\n" +
"int main() {" +
" chdir(\"" + tmpDir + "\");" +
" rmdir(\"newDir\");" +
" return 0;" +
"}", null);
assertEquals(1, fileAccesses.length);
assertEquals(1, fileDeletes.length);
String dirName = fileMgr.getPathName(fileDeletes[0]);
assertEquals(tmpDir + "/newDir", dirName);
assertEquals(PathType.TYPE_DIR, fileMgr.getPathType(fileDeletes[0]));
/*
* Fail to make a directory - should not be logged.
*/
traceOneProgram(
"#include <unistd.h>\n" +
"int main() {" +
" rmdir(\"/invalid/dir\");" +
" return 0;" +
"}", null);
assertEquals(0, fileAccesses.length);
}
/*-------------------------------------------------------------------------------------*/
}