const TestCase = require("mocha/lib/test.js");
const Suite = require("mocha/lib/suite.js");
const Deployer = require("truffle-deployer");
const find_contracts = require("truffle-contract-sources");
const compile = require("truffle-compile");
const abi = require("web3-eth-abi");
const series = require("async").series;
const path = require("path");
const semver = require("semver");

let SafeSend;

const SolidityTest = {
  define(abstraction, dependency_paths, runner, mocha) {
    const self = this;

    const suite = new Suite(abstraction.contract_name, {});
    suite.timeout(runner.BEFORE_TIMEOUT);

    // Set up our runner's needs first.
    suite.beforeAll("prepare suite", function(done) {
      series(
        [
          runner.initialize.bind(runner),
          self.compileNewAbstractInterface.bind(this, runner),
          self.deployTestDependencies.bind(
            this,
            abstraction,
            dependency_paths,
            runner
          )
        ],
        done
      );
    });

    suite.beforeEach("before test", function(done) {
      runner.startTest(this, done);
    });

    // Function that decodes raw logs from unlinked third party assertion
    // libraries and returns usable TestEvent logs
    function decodeTestEvents(result) {
      if (result.logs.length) return result.logs;

      const logs = [];
      const signature = web3.utils.sha3("TestEvent(bool,string)");

      result.receipt.logs.forEach(log => {
        if (log.topics.length === 2 && log.topics[0] === signature) {
          const decoded = {
            event: "TestEvent",
            args: {
              result: abi.decodeLog(["bool"], log.topics[1], log.topics)[0],
              message: abi.decodeLog(["string"], log.data, log.topics)[0]
            }
          };
          logs.push(decoded);
        }
      });
      return logs;
    }

    // Function that checks transaction logs to see if a test failed.
    function processResult(result) {
      result.logs = decodeTestEvents(result);

      result.logs.forEach(log => {
        if (log.event === "TestEvent" && !log.args.result)
          throw new Error(log.args.message);
      });
    }

    // Add functions from test file.
    abstraction.abi.forEach(item => {
      if (item.type !== "function") return;

      ["beforeAll", "beforeEach", "afterAll", "afterEach"].forEach(fn_type => {
        if (item.name.indexOf(fn_type) === 0) {
          suite[fn_type](item.name, () => {
            return abstraction
              .deployed()
              .then(deployed => {
                return deployed[item.name]();
              })
              .then(processResult);
          });
        }
      });

      if (item.name.indexOf("test") === 0) {
        const test = new TestCase(item.name, () => {
          return abstraction
            .deployed()
            .then(deployed => {
              return deployed[item.name]();
            })
            .then(processResult);
        });

        test.timeout(runner.TEST_TIMEOUT);
        suite.addTest(test);
      }
    });

    suite.afterEach("after test", function(done) {
      runner.endTest(this, done);
    });

    mocha.suite.addSuite(suite);
  },

  compileNewAbstractInterface(runner, callback) {
    find_contracts(runner.config.contracts_directory, err => {
      if (err) return callback(err);

      const config = runner.config;
      if (!config.compilers.solc.version) SafeSend = "NewSafeSend.sol";
      else if (semver.lt(semver.coerce(config.compilers.solc.version), "0.5.0"))
        SafeSend = "OldSafeSend.sol";
      else SafeSend = "NewSafeSend.sol";

      compile.with_dependencies(
        runner.config.with({
          paths: [
            path.join(__dirname, "Assert.sol"),
            path.join(__dirname, "AssertAddress.sol"),
            path.join(__dirname, "AssertAddressArray.sol"),
            // path.join(__dirname, "AssertAddressPayableArray.sol"), only compatible w/ ^0.5.0
            path.join(__dirname, "AssertBalance.sol"),
            path.join(__dirname, "AssertBool.sol"),
            path.join(__dirname, "AssertBytes32.sol"),
            path.join(__dirname, "AssertBytes32Array.sol"),
            path.join(__dirname, "AssertGeneral.sol"),
            path.join(__dirname, "AssertInt.sol"),
            path.join(__dirname, "AssertIntArray.sol"),
            path.join(__dirname, "AssertString.sol"),
            path.join(__dirname, "AssertUint.sol"),
            path.join(__dirname, "AssertUintArray.sol"),
            "truffle/DeployedAddresses.sol", // generated by deployed.js
            path.join(__dirname, SafeSend)
          ],
          quiet: true
        }),
        (err, contracts) => {
          if (err) return callback(err);

          // Set network values.
          Object.keys(contracts).forEach(name => {
            contracts[name].network_id = runner.config.network_id;
            contracts[name].default_network = runner.config.default_network;
          });

          runner.config.artifactor
            .saveAll(contracts, runner.config.contracts_build_directory)
            .then(() => {
              callback();
            })
            .catch(callback);
        }
      );
    });
  },

  deployTestDependencies(abstraction, dependency_paths, runner, callback) {
    const deployer = new Deployer(
      runner.config.with({
        logger: { log() {} }
      })
    );

    const testLibraries = [
      "Assert",
      "AssertAddress",
      "AssertAddressArray",
      //      "AssertAddressPayableArray", only compatible w/ ^0.5.0
      "AssertBalance",
      "AssertBool",
      "AssertBytes32",
      "AssertBytes32Array",
      "AssertGeneral",
      "AssertInt",
      "AssertIntArray",
      "AssertString",
      "AssertUint",
      "AssertUintArray",
      "DeployedAddresses"
    ];

    const testAbstractions = testLibraries.map(name =>
      runner.config.resolver.require(`truffle/${name}.sol`)
    );

    SafeSend = runner.config.resolver.require(SafeSend);

    for (const testLib of testAbstractions) {
      deployer.deploy(testLib);
      deployer.link(testLib, abstraction);
    }

    dependency_paths.forEach(dependency_path => {
      const dependency = runner.config.resolver.require(dependency_path);

      if (dependency.isDeployed()) deployer.link(dependency, abstraction);
    });

    let deployed;
    deployer
      .deploy(abstraction)
      .then(() => {
        return abstraction.deployed();
      })
      .then(instance => {
        deployed = instance;
        if (deployed.initialBalance) {
          return deployed.initialBalance.call();
        } else {
          return 0;
        }
      })
      .then(balance => {
        if (balance !== 0) {
          return deployer
            .deploy(SafeSend, deployed.address, {
              value: balance
            })
            .then(() => {
              return SafeSend.deployed();
            })
            .then(safesend => {
              return safesend.deliver();
            });
        }
      });

    deployer
      .start()
      .then(() => {
        callback();
      })
      .catch(callback);
  }
};

module.exports = SolidityTest;

@@ -5021,29 +5021,16 @@
 tifactor
-%0A            
 .saveAll
@@ -5043,78 +5043,11 @@
 acts
-, runner.config.contracts_build_directory)%0A            .then(() =%3E %7B%0A 
+);%0A
     
@@ -5048,27 +5048,24 @@
 ;%0A          
-   
 callback();%0A
@@ -5068,53 +5068,8 @@
 ();%0A
-            %7D)%0A            .catch(callback);%0A
     

bring sol unit testing artifact saving in line