UNPKG

truffle

Version:

Truffle - Simple development framework for Ethereum

400 lines (326 loc) 12.2 kB
const CommandRunner = require("../commandRunner"); const MemoryLogger = require("../MemoryLogger"); const path = require("path"); const assert = require("assert"); const Server = require("../server"); const sandbox = require("../sandbox"); const fse = require("fs-extra"); const { connect } = require("@truffle/db"); const gql = require("graphql-tag"); const pascalCase = require("pascal-case"); const Config = require("@truffle/config"); let config, artifactPaths, initialTimes, finalTimes, output, cleanupSandboxDir; describe("repeated compilation of contracts with inheritance [ @standalone ]", function () { const mapping = {}; const project = path.join(__dirname, "../../sources/inheritance"); const names = [ "Root.sol", "Branch.sol", "LeafA.sol", "LeafB.sol", "LeafC.sol", "SameFile1.sol", "SameFile2.sol", "LibraryA.sol", "Abi.abi.json" ]; const logger = new MemoryLogger(); // ----------------------- Utils ----------------------------- function processErr(err, output) { if (err) { console.log(output); throw new Error(err); } } function waitSecond() { return new Promise((resolve, _reject) => setTimeout(() => resolve(), 1250)); } function getSource(key) { return fse.readFileSync(mapping[key].sourcePath); } function getArtifactStats() { const stats = {}; names.forEach(key => { const mDate = fse.statSync(mapping[key].artifactPath).mtime.getTime(); stats[key] = mDate; }); return stats; } function touchSource(key) { const source = getSource(key); fse.writeFileSync(mapping[key].sourcePath, source); } function hasBeenUpdated(fileName) { return initialTimes[fileName] < finalTimes[fileName]; } // ----------------------- Setup ----------------------------- before(async function () { await Server.start(); }); after(async function () { await Server.stop(); }); beforeEach("set up sandbox and do initial compile", async function () { this.timeout(30000); ({ config, cleanupSandboxDir } = await sandbox.create(project)); config.network = "development"; config.logger = logger; // create artifact path array artifactPaths = names.map(name => { const basename = path.basename(path.basename(name, ".sol"), ".abi.json"); return path.join(config.contracts_build_directory, `${basename}.json`); }); // create mapping from name to source path names.forEach((name, i) => { mapping[name] = {}; mapping[name].artifactPath = artifactPaths[i]; mapping[name].sourcePath = path.join(config.contracts_directory, name); }); try { await CommandRunner.run("compile", config); } catch (error) { output = logger.contents(); processErr(error, output); } initialTimes = getArtifactStats(); // mTime resolution on 6.9.1 is 1 sec. await waitSecond(); }); afterEach(function () { cleanupSandboxDir(); }); // -------------Inheritance Graph ----------------------------- // | // LibA LeafA | SameFile1 - LeafC // / / \ | // Root* - Branch - - LeafC | SameFile2 // \ \ / | // Abi LeafB | // | // ------------------------------------------------------------ it("updates only Root when Root is touched", async function () { this.timeout(30000); touchSource("Root.sol"); await CommandRunner.run("compile", config); output = logger.contents(); finalTimes = getArtifactStats(); try { assert(hasBeenUpdated("Root.sol"), "Should update root"); for (const file of Object.keys(mapping)) { if (file !== "Root.sol") { assert(!hasBeenUpdated(file), `Should not update ${file}`); } } } catch (err) { err.message += "\n\n" + output; throw new Error(err); } }); // -------------Inheritance Graph ----------------------------- // | // LibA* LeafA | SameFile1 - LeafC // / / \ | // Root* - Branch - - LeafC | SameFile2 // \ \ / | // Abi LeafB | // | // ------------------------------------------------------------ it("updates Root and Library when Library is touched", async function () { this.timeout(30000); touchSource("LibraryA.sol"); await CommandRunner.run("compile", config); output = logger.contents(); finalTimes = getArtifactStats(); try { assert(hasBeenUpdated("Root.sol"), "Should update root"); assert(hasBeenUpdated("LibraryA.sol"), "Should update LibraryA"); for (const file of Object.keys(mapping)) { if (file !== "Root.sol" && file !== "LibraryA.sol") { assert(!hasBeenUpdated(file), `Should not update ${file}`); } } } catch (err) { err.message += "\n\n" + output; throw new Error(err); } }); // -------------Inheritance Graph ----------------------------- // | // LibA LeafA | SameFile1 - LeafC // / / \ | // Root* - Branch* - - LeafC | SameFile2 // \ \ / | // Abi LeafB | // | // ------------------------------------------------------------ it("updates Branch and Root when Branch is touched", async function () { this.timeout(30000); touchSource("Branch.sol"); await CommandRunner.run("compile", config); output = logger.contents(); finalTimes = getArtifactStats(); try { assert(hasBeenUpdated("Root.sol"), "Should update root"); assert(hasBeenUpdated("Branch.sol"), "Should update Branch"); for (const file of Object.keys(mapping)) { if (file !== "Root.sol" && file !== "Branch.sol") { assert(!hasBeenUpdated(file), `Should not update ${file}`); } } } catch (err) { err.message += "\n\n" + output; throw new Error(err); } }); // -------------Inheritance Graph ----------------------------- // | // LibA LeafA* | SameFile1 - LeafC // / / \ | // Root* - Branch* - - LeafC | SameFile2 // \ \ / | // Abi LeafB | // | // ------------------------------------------------------------ it("updates LeafA, Branch and Root when LeafA is touched", async function () { this.timeout(30000); touchSource("LeafA.sol"); await CommandRunner.run("compile", config); output = logger.contents(); finalTimes = getArtifactStats(); try { assert(hasBeenUpdated("LeafA.sol"), "Should update LeafA"); assert(hasBeenUpdated("Branch.sol"), "Should update Branch"); assert(hasBeenUpdated("Root.sol"), "Should update root"); for (const file of Object.keys(mapping)) { if ( file !== "Root.sol" && file !== "Branch.sol" && file !== "LeafA.sol" ) { assert(!hasBeenUpdated(file), `Should not update ${file}`); } } } catch (err) { err.message += "\n\n" + output; throw new Error(err); } }); // -------------Inheritance Graph ----------------------------- // | // LibA LeafA* | SameFile1* - LeafC* // / / \ | // Root* - Branch* - - LeafC* | SameFile2* // \ \ / | // Abi LeafB* | // | // ------------------------------------------------------------ it("updates everything except LibraryA and Abi when LeafC is touched", async function () { this.timeout(30000); touchSource("LeafC.sol"); await CommandRunner.run("compile", config); output = logger.contents(); finalTimes = getArtifactStats(); try { assert(!hasBeenUpdated("LibraryA.sol"), "Should not update LibraryA"); assert(!hasBeenUpdated("Abi.abi.json"), "Should not update Abi"); for (const file of Object.keys(mapping)) { if (file !== "LibraryA.sol" && file !== "Abi.abi.json") { assert(hasBeenUpdated(file), `Should not update ${file}`); } } } catch (err) { err.message += "\n\n" + output; throw new Error(err); } }); // -------------Inheritance Graph ----------------------------- // | // LibA LeafA* | SameFile1* - LeafC* // / / \ | // Root* - Branch* - - LeafC* | SameFile2* // \ \ / | // Abi LeafB* | // | // ------------------------------------------------------------ it("updates Root and Abi when Abi is touched", async function () { this.timeout(30000); touchSource("Abi.abi.json"); await CommandRunner.run("compile", config); output = logger.contents(); finalTimes = getArtifactStats(); try { assert(hasBeenUpdated("Root.sol"), "Should update root"); assert(hasBeenUpdated("Abi.abi.json"), "Should update Abi"); for (const file of Object.keys(mapping)) { if (file !== "Root.sol" && file !== "Abi.abi.json") { assert(!hasBeenUpdated(file), `Should not update ${file}`); } } } catch (err) { err.message += "\n\n" + output; throw new Error(err); } }); }); describe("compilation with db enabled", async () => { let config, project; const logger = new MemoryLogger(); function checkForDb() { const truffleDataDirectory = Config.getTruffleDataDirectory(); const dbPath = path.join(truffleDataDirectory, ".db"); const dbExists = fse.pathExistsSync(dbPath); return dbExists; } before(async function () { await Server.start(); }); after(async function () { await Server.stop(); }); beforeEach("set up sandbox and do initial compile", async function () { this.timeout(30000); project = path.join(__dirname, "../../sources/db_enabled"); ({ config, cleanupSandboxDir } = await sandbox.create(project)); try { await CommandRunner.run("compile", config); } catch (error) { output = logger.contents(); console.log(output); throw new Error(error); } }); afterEach(function () { cleanupSandboxDir(); }); it("creates a populated .db directory when db is enabled", async function () { this.timeout(12000); const dbExists = checkForDb(); assert(dbExists === true); }); it("adds contracts to the db", async function () { this.timeout(12000); const GetAllContracts = gql` query getAllContracts { contracts { name } } `; // connect to DB const db = connect(); const results = await db.execute(GetAllContracts, {}); // number of contracts matches number of contracts in the project directory // (plus one library in this one) const names = ["Contract", "InnerLibrary", "Migrations", "RelativeImport"]; for (const name of names) { assert(results.data.contracts.some(contract => contract.name === name)); } // contract names in project exist in new .db contracts file const resultsNames = results.data.contracts.map(a => a.name); const contractNames = fse.readdirSync(path.join(project, "contracts")); contractNames.map(name => { const processedName = pascalCase(name.split(".")[0]); assert(resultsNames.includes(processedName)); }); }); });