text: 'use strict'
  
  const fs = require('fs')
  const os = require('os')
  const fse = require('../../')
  const path = require('path')
  const assert = require('assert')
  const crypto = require('crypto')
  
  /* global afterEach, beforeEach, describe, it */
  
  const SIZE = 16 * 64 * 1024 + 7
  let TEST_DIR = ''
  
  describe('fs-extra', () => {
    beforeEach(done => {
      TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy')
      fse.emptyDir(TEST_DIR, done)
    })
  
    afterEach(done => fse.remove(TEST_DIR, done))
  
    describe('+ copy()', () => {
      it('should return an error if src and dest are the same', done => {
        const fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_copy')
        const fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy')
        fse.ensureFileSync(fileSrc)
  
        fse.copy(fileSrc, fileDest, err => {
          assert.strictEqual(err.message, 'Source and destination must not be the same.')
          done()
        })
      })
  
      it('should error when overwrite=false and file exists', done => {
        const src = path.join(TEST_DIR, 'src.txt')
        const dest = path.join(TEST_DIR, 'dest.txt')
  
        fse.ensureFileSync(src)
        fse.ensureFileSync(dest)
        fse.copy(src, dest, { overwrite: false, errorOnExist: true }, err => {
          assert(err)
          done()
        })
      })
  
      it('should error when overwrite=false and file exists in a dir', done => {
        const src = path.join(TEST_DIR, 'src', 'sfile.txt')
        const dest = path.join(TEST_DIR, 'dest', 'dfile.txt')
  
        fse.ensureFileSync(src)
        fse.ensureFileSync(dest)
        fse.copy(src, dest, { overwrite: false, errorOnExist: true }, err => {
          assert(err)
          done()
        })
      })
  
      describe('> when src is a file', () => {
        it('should copy the file asynchronously', done => {
          const fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_src')
          const fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy')
          fs.writeFileSync(fileSrc, crypto.randomBytes(SIZE))
          const srcMd5 = crypto.createHash('md5').update(fs.readFileSync(fileSrc)).digest('hex')
          let destMd5 = ''
  
          fse.copy(fileSrc, fileDest, err => {
            assert(!err)
            destMd5 = crypto.createHash('md5').update(fs.readFileSync(fileDest)).digest('hex')
            assert.strictEqual(srcMd5, destMd5)
            done()
          })
        })
  
        it('should work with promises', () => {
          const fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_src')
          const fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy')
          fs.writeFileSync(fileSrc, crypto.randomBytes(SIZE))
          const srcMd5 = crypto.createHash('md5').update(fs.readFileSync(fileSrc)).digest('hex')
          let destMd5 = ''
  
          return fse.copy(fileSrc, fileDest).then(() => {
            destMd5 = crypto.createHash('md5').update(fs.readFileSync(fileDest)).digest('hex')
            assert.strictEqual(srcMd5, destMd5)
          })
        })
  
        it('should return an error if src file does not exist', done => {
          const fileSrc = 'we-simply-assume-this-file-does-not-exist.bin'
          const fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy')
  
          fse.copy(fileSrc, fileDest, err => {
            assert(err)
            done()
          })
        })
  
        it('should copy to a destination file with two \'$\' characters in name (eg: TEST_fs-extra_$$_copy)', done => {
          const fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_src')
          const fileDest = path.join(TEST_DIR, 'TEST_fs-extra_$$_copy')
  
          fs.writeFileSync(fileSrc, '')
  
          fse.copy(fileSrc, fileDest, err => {
            assert(!err)
            fs.statSync(fileDest)
            done()
          })
        })
  
        describe('> when the destination dir does not exist', () => {
          it('should create the destination directory and copy the file', done => {
            const src = path.join(TEST_DIR, 'file.txt')
            const dest = path.join(TEST_DIR, 'this/path/does/not/exist/copied.txt')
            const data = 'did it copy?\n'
  
            fs.writeFileSync(src, data, 'utf8')
  
            fse.copy(src, dest, err => {
              const data2 = fs.readFileSync(dest, 'utf8')
              assert.strictEqual(data, data2)
              done(err)
            })
          })
        })
      })
  
      describe('> when src is a directory', () => {
        describe('> when src directory does not exist', () => {
          it('should return an error', done => {
            const ts = path.join(TEST_DIR, 'this_dir_does_not_exist')
            const td = path.join(TEST_DIR, 'this_dir_really_does_not_matter')
            fse.copy(ts, td, err => {
              assert(err)
              done()
            })
          })
        })
  
        describe('> when dest exists and is a file', () => {
          it('should return an error', done => {
            const src = path.join(TEST_DIR, 'src')
            const dest = path.join(TEST_DIR, 'file.txt')
            fs.mkdirSync(src)
            fse.ensureFileSync(dest)
  
            fse.copy(src, dest, err => {
              assert.strictEqual(err.message, `Cannot overwrite non-directory '${dest}' with directory '${src}'.`)
              done()
            })
          })
        })
  
        it('should preserve symbolic links', done => {
          const src = path.join(TEST_DIR, 'src')
          const dest = path.join(TEST_DIR, 'dest')
          const srcTarget = path.join(TEST_DIR, 'destination')
          fse.mkdirSync(src)
          fse.mkdirSync(srcTarget)
          // symlink type is only used for Windows and the default is 'file'.
          // https://nodejs.org/api/fs.html#fs_fs_symlink_target_path_type_callback
          fse.symlinkSync(srcTarget, path.join(src, 'symlink'), 'dir')
  
          fse.copy(src, dest, err => {
            assert.ifError(err)
            const link = fs.readlinkSync(path.join(dest, 'symlink'))
            assert.strictEqual(link, srcTarget)
            done()
          })
        })
  
        it('should copy the directory asynchronously', done => {
          const FILES = 2
          const src = path.join(TEST_DIR, 'src')
          const dest = path.join(TEST_DIR, 'dest')
  
          fse.mkdirs(src, err => {
            assert(!err)
            for (let i = 0; i < FILES; ++i) {
              fs.writeFileSync(path.join(src, i.toString()), crypto.randomBytes(SIZE))
            }
  
            const subdir = path.join(src, 'subdir')
            fse.mkdirs(subdir, err => {
              assert(!err)
              for (let i = 0; i < FILES; ++i) {
                fs.writeFileSync(path.join(subdir, i.toString()), crypto.randomBytes(SIZE))
              }
  
              fse.copy(src, dest, err => {
                assert.ifError(err)
                assert(fs.existsSync(dest))
  
                for (let i = 0; i < FILES; ++i) {
                  assert(fs.existsSync(path.join(dest, i.toString())))
                }
  
                const destSub = path.join(dest, 'subdir')
                for (let j = 0; j < FILES; ++j) {
                  assert(fs.existsSync(path.join(destSub, j.toString())))
                }
  
                done()
              })
            })
          })
        })
  
        describe('> when the destination dir does not exist', () => {
          it('should create the destination directory and copy the file', done => {
            const src = path.join(TEST_DIR, 'data/')
            fse.mkdirsSync(src)
            const d1 = 'file1'
            const d2 = 'file2'
  
            fs.writeFileSync(path.join(src, 'f1.txt'), d1)
            fs.writeFileSync(path.join(src, 'f2.txt'), d2)
  
            const dest = path.join(TEST_DIR, 'this/path/does/not/exist/outputDir')
  
            fse.copy(src, dest, err => {
              const o1 = fs.readFileSync(path.join(dest, 'f1.txt'), 'utf8')
              const o2 = fs.readFileSync(path.join(dest, 'f2.txt'), 'utf8')
  
              assert.strictEqual(d1, o1)
              assert.strictEqual(d2, o2)
  
              done(err)
            })
          })
        })
  
        describe('> when src dir does not exist', () => {
          it('should return an error', done => {
            fse.copy('/does/not/exist', '/something/else', err => {
              assert(err instanceof Error)
              done()
            })
          })
        })
      })
  
      describe('> when filter is used', () => {
        it('should do nothing if filter fails', done => {
          const srcDir = path.join(TEST_DIR, 'src')
          const srcFile = path.join(srcDir, 'srcfile.css')
          fse.outputFileSync(srcFile, 'src contents')
          const destDir = path.join(TEST_DIR, 'dest')
          const destFile = path.join(destDir, 'destfile.css')
          const filter = s => path.extname(s) !== '.css' && !fs.statSync(s).isDirectory()
  
          fse.copy(srcFile, destFile, filter, err => {
            assert.ifError(err)
            assert(!fs.existsSync(destDir))
            done()
          })
        })
  
        it('should only copy files allowed by filter fn', done => {
          const srcFile1 = path.join(TEST_DIR, '1.css')
          fs.writeFileSync(srcFile1, '')
          const destFile1 = path.join(TEST_DIR, 'dest1.css')
          const filter = s => s.split('.').pop() !== 'css'
  
          fse.copy(srcFile1, destFile1, filter, err => {
            assert(!err)
            assert(!fs.existsSync(destFile1))
            done()
          })
        })
  
        it('accepts options object in place of filter', done => {
          const srcFile1 = path.join(TEST_DIR, '1.jade')
          fs.writeFileSync(srcFile1, '')
          const destFile1 = path.join(TEST_DIR, 'dest1.jade')
          const options = { filter: s => /.html$|.css$/i.test(s) }
  
          fse.copy(srcFile1, destFile1, options, (err) => {
            assert(!err)
            assert(!fs.existsSync(destFile1))
            done()
          })
        })
  
        it('allows filter fn to return a promise', done => {
          const srcFile1 = path.join(TEST_DIR, '1.css')
          fs.writeFileSync(srcFile1, '')
          const destFile1 = path.join(TEST_DIR, 'dest1.css')
          const filter = s => Promise.resolve(s.split('.').pop() !== 'css')
  
          fse.copy(srcFile1, destFile1, filter, err => {
            assert(!err)
            assert(!fs.existsSync(destFile1))
            done()
          })
        })
  
        it('should apply filter recursively', done => {
          const FILES = 2
          // Don't match anything that ends with a digit higher than 0:
          const filter = s => /(0|\D)$/i.test(s)
  
          const src = path.join(TEST_DIR, 'src')
          fse.mkdirsSync(src)
  
          for (let i = 0; i < FILES; ++i) {
            fs.writeFileSync(path.join(src, i.toString()), crypto.randomBytes(SIZE))
          }
  
          const subdir = path.join(src, 'subdir')
          fse.mkdirsSync(subdir)
  
          for (let i = 0; i < FILES; ++i) {
            fs.writeFileSync(path.join(subdir, i.toString()), crypto.randomBytes(SIZE))
          }
          const dest = path.join(TEST_DIR, 'dest')
          fse.copy(src, dest, filter, err => {
            assert(!err)
  
            assert(fs.existsSync(dest))
            assert(FILES > 1)
  
            for (let i = 0; i < FILES; ++i) {
              if (i === 0) {
                assert(fs.existsSync(path.join(dest, i.toString())))
              } else {
                assert(!fs.existsSync(path.join(dest, i.toString())))
              }
            }
  
            const destSub = path.join(dest, 'subdir')
  
            for (let j = 0; j < FILES; ++j) {
              if (j === 0) {
                assert(fs.existsSync(path.join(destSub, j.toString())))
              } else {
                assert(!fs.existsSync(path.join(destSub, j.toString())))
              }
            }
            done()
          })
        })
  
        it('should apply filter to directory names', done => {
          const IGNORE = 'ignore'
          const filter = p => !~p.indexOf(IGNORE)
  
          const src = path.join(TEST_DIR, 'src')
          fse.mkdirsSync(src)
  
          const ignoreDir = path.join(src, IGNORE)
          fse.mkdirsSync(ignoreDir)
  
          fse.writeFileSync(path.join(ignoreDir, 'file'), crypto.randomBytes(SIZE))
  
          const dest = path.join(TEST_DIR, 'dest')
  
          fse.copy(src, dest, filter, err => {
            assert.ifError(err)
            assert(!fs.existsSync(path.join(dest, IGNORE)), 'directory was not ignored')
            assert(!fs.existsSync(path.join(dest, IGNORE, 'file')), 'file was not ignored')
            done()
          })
        })
  
        it('should apply filter when it is applied only to dest', done => {
          const timeCond = new Date().getTime()
  
          const filter = (s, d) => fs.statSync(d).mtime.getTime() < timeCond
  
          const src = path.join(TEST_DIR, 'src')
          fse.mkdirsSync(src)
          const subdir = path.join(src, 'subdir')
          fse.mkdirsSync(subdir)
  
          const dest = path.join(TEST_DIR, 'dest')
  
          setTimeout(() => {
            fse.mkdirsSync(dest)
  
            fse.copy(src, dest, filter, err => {
              assert(!err)
              assert(!fs.existsSync(path.join(dest, 'subdir')))
              done()
            })
          }, 1000)
        })
  
        it('should apply filter when it is applied to both src and dest', done => {
          const timeCond = new Date().getTime()
          const filter = (s, d) => s.split('.').pop() !== 'css' && fs.statSync(path.dirname(d)).mtime.getTime() > timeCond
  
          const dest = path.join(TEST_DIR, 'dest')
          setTimeout(() => {
            fse.mkdirsSync(dest)
  
            const srcFile1 = path.join(TEST_DIR, '1.html')
            const srcFile2 = path.join(TEST_DIR, '2.css')
            const srcFile3 = path.join(TEST_DIR, '3.jade')
  
            fse.writeFileSync(srcFile1, '')
            fse.writeFileSync(srcFile2, '')
            fse.writeFileSync(srcFile3, '')
  
            const destFile1 = path.join(dest, 'dest1.html')
            const destFile2 = path.join(dest, 'dest2.css')
            const destFile3 = path.join(dest, 'dest3.jade')
  
            fse.copy(srcFile1, destFile1, filter, err => {
              assert(!err)
              assert(fs.existsSync(destFile1))
  
              fse.copy(srcFile2, destFile2, filter, err => {
                assert(!err)
                assert(!fs.existsSync(destFile2))
  
                fse.copy(srcFile3, destFile3, filter, err => {
                  assert(!err)
                  assert(fs.existsSync(destFile3))
                  done()
                })
              })
            })
          }, 1000)
        })
      })
    })
  })
  