diff --git a/README.md b/README.md
index 6784ff8..fa56c0c 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# dir-tree 
+# dir-tree-scanner
 <a href="https://travis-ci.org/physicsLoveJava/dir-tree" >
  <img src='https://travis-ci.org/physicsLoveJava/dir-tree.svg?branch=master' alt='Build status'/>
 </a>
@@ -6,7 +6,7 @@
   <img src='https://codeclimate.com/github/physicsLoveJava/dir-tree/badges/gpa.svg' alt='Code Climate'>
 </a>
 
-**dir-tree** is a simple directory tree scanner for node, which supports directory level update, filters, display tree structure and so on.
+**dir-tree-scanner** is a simple directory tree scanner for node, which supports directory level update, filters, display tree structure and so on.
 
 # Install
 
diff --git a/lib/dir-tree.js b/lib/dir-tree.js
index 2954932..fffaa34 100644
--- a/lib/dir-tree.js
+++ b/lib/dir-tree.js
@@ -5,19 +5,16 @@
 var path = require('path');
 var fs = require('fs');
 var Q = require('q');
+var fileStat = require('./file-stat');
+var subFiles = require('./sub-files');
+var util = require('./options-util');
+var treeUtil = require('./treenode-util');
 
-function scan(dirPath, options){
-
-  var scanObj = {};
-  scanObj.dirs_queue = [];
-  scanObj.msg_queue = [];
-  scanObj.msg_timer = void 0;
-  options === undefined ? (options = defaultOptions()) : void 0;
-  scanObj.options = options;
+function scan(dirPath, scanObj){
 
   var deferred = Q.defer();
 
-  _checkIfDirectory(dirPath)
+  fileStat.isDirectory(dirPath)
       .then(function(data) {
       if (!data) {
         deferred.reject({message: 'path is not a directory'});
@@ -36,149 +33,22 @@ function scan(dirPath, options){
        });
     });
   return deferred.promise;
-};
+}
 
 function find(dirPath, options){
-  options === undefined ? (options = defaultOptions()) : void 0;
   return Q.Promise(function(resolve, reject, progress){
     var jqTree = {};
-    scan(dirPath, options).progress(function(data){
+    scan(dirPath, util.getDefaultScanObj(options))
+      .progress(function(data){
       progress(data);
-      updateTree(jqTree, data);
+      treeUtil.updateTree(jqTree, data);
     }).done(function(){
-      removeEmptyDirs(jqTree, options);
+      treeUtil.removeEmptyDirs(jqTree, util.getDefaultOption(options));
       resolve(jqTree);
     });
   });
 }
 
-function removeEmptyDirs(tree, options){
-  if(isOptionsFileEmpty(options.file)){
-    return;
-  }
-  var root = tree.data[0];
-  var stack = [];
-  //visit , 0 , 1  (unvisited, visited)
-  root.visit = 0;
-  root.cvisit = 0;
-  stack.push(root);
-  while(stack.length > 0){
-    var current = stack[stack.length - 1];
-    if(current.children.length === 0){
-      current.cvisit = 1;
-    }else{
-      var hasUnVisited = false;
-      for(var i = 0; i < current.children.length; i ++){
-        if(current.children[i].isDirectory){
-          if(!current.children[i].visit){
-            hasUnVisited = true;
-          }else if(current.children[i].visit === 0){
-            hasUnVisited = true;
-          }
-        }
-      }
-      if(!hasUnVisited){
-        current.cvisit = 1;
-      }
-    }
-    if(current.cvisit === 0){
-      for(var j = 0; j < current.children.length; j ++){
-        if(current.children[j].isDirectory){
-          current.children[j].visit = 0;
-          current.children[j].cvisit = 0;
-          stack.push(current.children[j]);
-        }
-      }
-    }else{
-      if(current.children.length === 0){
-        //visit current node
-        removeTreeNode(tree, current.name);
-      }
-      current.visit = 1;
-      stack.pop();
-    }
-  }
-};
-
-function removeTreeNode(tree, name){
-  var root = tree.data[0];
-  var queue = [];
-  queue.push({parent: {children: [root]}, real: root, index: 0});
-  while(queue.length > 0){
-      var current = queue.pop();
-      if(current.real.name === name){
-        current.parent.children.splice(current.index, 1);
-      }else{
-         for(var i = 0; i < current.real.children.length; i++){
-           if(current.real.children[i].isDirectory){
-             queue.push({parent: current.real, real: current.real.children[i], index: i});
-           }
-         }
-      }
-  }
-}
-
-function isOptionsFileEmpty(fileOption){
-  if(fileOption === undefined){
-    return true;
-  }
-  if(fileOption instanceof Array){
-    return fileOption.length === 0;
-  }
-  return false;
-}
-
-function updateTree(tree, data) {
-  if (tree.data === undefined) {
-    if(data === undefined){
-      throw new Error('data is undefined');
-      return;
-    }
-    tree.data = [];
-    tree.data.push(getFormatNode(data));
-    tree.toUpdate = data.toUpdate;
-    tree.current = data.parent;
-  } else {
-    if (tree.toUpdate.length === 0) {
-      throw new Error('should not notify data when there is no sub node to update.');
-    }
-
-    for (var i = 0; i < tree.toUpdate; i++) {
-      if (tree.toUpdate[i] === data.parent) {
-        tree.toUpdate.splice(i, 1);
-      }
-    }
-    data.toUpdate.forEach(function (info) {
-      tree.toUpdate.push(info);
-    });
-    updateSubNode(tree, data);
-  }
-
-  function getFormatNode(data){
-    return {name: data.parent, children: data.children.map(function(child){
-      return  {name: child.filePath, isDirectory: child.isDirectory};
-    })};
-  }
-
-  function updateSubNode(tree, data) {
-    var queue = [];
-    queue.push(tree.data[0]);
-    while(queue.length > 0){
-      var current = queue.shift();
-      if(current.name === data.parent){
-        current.children = getFormatNode(data).children;
-        return;
-      }else{
-        if(current.children){
-          current.children.map(function(child){
-            queue.push(child);
-          });
-        }
-      }
-    }
-  }
-}
-
 function checkAndTravel(scanObj, deferred){
   if(scanObj.dirs_queue.length > 0){
     var first = scanObj.dirs_queue.shift();
@@ -189,7 +59,7 @@ function checkAndTravel(scanObj, deferred){
 }
 
 function findDirectSubDirectories(scanObj, dirPath){
-  var subPm = findDirectSubFiles(dirPath, scanObj.options)
+  return subFiles.findDirectSubFiles(dirPath, scanObj.options)
     .then(function(infoList){
       infoList.children.forEach(function(info){
         if(info.isDirectory){
@@ -199,86 +69,6 @@ function findDirectSubDirectories(scanObj, dirPath){
       scanObj.msg_queue.push(infoList);
       return infoList;
     });
-  return Q.all([subPm]);
-}
-
- function findDirectSubFiles(dirPath, options){
-
-  return Q.Promise(function(resolve, reject){
-        fs.readdir(dirPath, function(err, fileList){
-            if(err){
-              reject(err);return;
-            }
-
-          var promises = [];
-          fileList.forEach(function(file){
-            if(filterFileType(file, options)){
-              promises.push(fileStat(path.join(dirPath, file)));
-            }
-          });
-          return Q.all(promises).then(function(infoList){
-            var result = {};
-            result.parent = dirPath;
-            result.children = infoList;
-            result.toUpdate = (function(){
-              var result = [];
-              infoList.forEach(function(info){
-                if(info.isDirectory){
-                  result.push(info);
-                }
-              });
-              return result;
-            }());
-            resolve(result);
-          });
-        });
-  });
-
-}
-
-function filterFileType(file, options){
-    var suffix = options.file;
-    if(suffix === undefined){
-      throw new Error('file option is not set.');
-    }
-    var extIndex = file.lastIndexOf('.');
-    if(extIndex === -1){
-      return true;
-    }
-    var ext = file.substr(extIndex);
-    if(suffix instanceof Array){
-      if(suffix.length === 0){
-        return true;
-      }
-      return suffix.filter(function(x){
-        return x.toUpperCase() === ext.toUpperCase();
-      }).length > 0;
-    }
-    if(suffix instanceof RegExp){
-      return suffix.test(file);
-    }
-    return false;
-}
-
-function _checkIfDirectory(filePath){
-  return Q.Promise(function(resolve, reject){
-    fileStat(filePath).then(function(data){
-      resolve(data.isDirectory);
-    }, function(err){
-      reject(err);
-    });
-  });
-}
-
-function fileStat(filePath){
-  return Q.Promise(function(resolve, reject){
-    fs.stat(filePath, function(err, info){
-      if(err) reject(err);
-      else{
-        resolve({filePath: filePath, isDirectory: info.isDirectory()});
-      }
-    });
-  });
 }
 
 function notifyTask(scanObj, latency, cb){
@@ -301,21 +91,11 @@ function notifyTask(scanObj, latency, cb){
       }else{
         clearInterval(timer);
         resolve();
-        return;
       }
     }, latency);
   });
 }
 
-function defaultOptions(){
-  return {
-      file: [],
-      directory: [],
-      layout: 'flat' // flat hierarchy
-
-  };
-}
-
 function display(data){
   if(! data instanceof Array){
     throw new Error('input data format is illegal.');
@@ -336,7 +116,7 @@ function displayRoot(data, level){
     displayRoot(data[i].children, xLevel);
     xLevel --;
   }
-};
+}
 
 function displayLevel(level){
   if(level <= 0){
@@ -351,9 +131,6 @@ function displayLevel(level){
 }
 
 
-module.exports._checkIfDirectory = _checkIfDirectory;
-module.exports.subDirectory = findDirectSubFiles;
 module.exports.scan = scan;
 module.exports.find = find;
 module.exports.display = display;
-module.exports.removeTreeNode = removeTreeNode;
diff --git a/lib/file-stat.js b/lib/file-stat.js
new file mode 100644
index 0000000..a1599b7
--- /dev/null
+++ b/lib/file-stat.js
@@ -0,0 +1,31 @@
+/**
+ * Created by LuJian on 2016/6/28.
+ */
+
+var Q = require('q');
+var fs = require('fs');
+
+module.exports = {
+    fileInfo: function(filePath){
+        return Q.Promise(function(resolve, reject){
+            fs.stat(filePath, function(err, info){
+                if(err) reject(err);
+                else{
+                    resolve({filePath: filePath, isDirectory: info.isDirectory()});
+                }
+            });
+        });
+    },
+    isDirectory: function(filePath){
+        var self = this;
+        return Q.Promise(function(resolve, reject){
+            self.fileInfo(filePath).then(function(data){
+                resolve(data.isDirectory);
+            }, function(err){
+                reject(err);
+            });
+        });
+    }
+};
+
+
diff --git a/lib/options-util.js b/lib/options-util.js
new file mode 100644
index 0000000..960fd38
--- /dev/null
+++ b/lib/options-util.js
@@ -0,0 +1,47 @@
+/**
+ * Created by LuJian on 2016/7/4.
+ */
+
+var ScanObj = require('./scanObj');
+var opDefault = require('./options');
+
+module.exports = {
+    filterFileType : function(file, options){
+        var suffix = options.file;
+        if(suffix === undefined){
+            throw new Error('file option is not set.');
+        }
+        var extIndex = file.lastIndexOf('.');
+        if(extIndex === -1){
+            return true;
+        }
+        var ext = file.substr(extIndex);
+        if(suffix instanceof Array){
+            if(suffix.length === 0){
+                return true;
+            }
+            return suffix.filter(function(x){
+                    return x.toUpperCase() === ext.toUpperCase();
+                }).length > 0;
+        }
+        if(suffix instanceof RegExp){
+            return suffix.test(file);
+        }
+        return false;
+    },
+    isOptionsFileEmpty : function(fileOption){
+        if(!fileOption){
+            return true;
+        }
+        if(fileOption instanceof Array){
+            return fileOption.length === 0;
+        }
+        return false;
+    },
+    getDefaultScanObj: function(options){
+        return ScanObj(options);
+    },
+    getDefaultOption: function(options){
+        return opDefault.defaultOptions(options);
+    }
+}
\ No newline at end of file
diff --git a/lib/options.js b/lib/options.js
new file mode 100644
index 0000000..90a5814
--- /dev/null
+++ b/lib/options.js
@@ -0,0 +1,18 @@
+/**
+ * Created by LuJian on 2016/7/5.
+ */
+
+
+module.exports = {
+    defaultOptions: function(options){
+        function getDefaultOptions(){
+            return {
+                file: [],
+                directory: [],
+                layout: 'flat' // flat hierarchy
+
+            };
+        }
+        return options || getDefaultOptions();
+    }
+}
\ No newline at end of file
diff --git a/lib/scanObj.js b/lib/scanObj.js
new file mode 100644
index 0000000..8a97c56
--- /dev/null
+++ b/lib/scanObj.js
@@ -0,0 +1,16 @@
+/**
+ * Created by LuJian on 2016/7/4.
+ */
+
+var opDefault = require('./options');
+
+function ScanObj(options){
+    var scanObj = {};
+    scanObj.dirs_queue = [];
+    scanObj.msg_queue = [];
+    scanObj.msg_timer = undefined;
+    scanObj.options = opDefault.defaultOptions(options);
+    return scanObj;
+}
+
+module.exports = ScanObj;
\ No newline at end of file
diff --git a/lib/sub-files.js b/lib/sub-files.js
new file mode 100644
index 0000000..b25cb86
--- /dev/null
+++ b/lib/sub-files.js
@@ -0,0 +1,68 @@
+/**
+ * Created by LuJian on 2016/7/4.
+ */
+
+var Q = require('q');
+var fs = require('fs');
+var path = require('path');
+var fileStat = require('./file-stat');
+var util = require('./options-util');
+
+module.exports = {
+    findDirectSubFiles : function(dirPath, options){
+        return Q.Promise(function(resolve, reject){
+            fs.readdir(dirPath, function(err, fileList){
+                if(err){
+                    reject(err);return;
+                }
+
+                var promises = [];
+                if(!fileList){
+                    throw new Error('file list is undefined');
+                }
+                fileList.forEach(function(file){
+                    if(util.filterFileType(file, options)){
+                        promises.push(fileStat.fileInfo(path.join(dirPath, file)));
+                    }
+                });
+                return Q.allSettled(promises).then(function(infoList){
+                    var result = {};
+                    result.parent = dirPath;
+                    result.children = (function(){
+                        var result = [];
+                        infoList.forEach(function(single){
+                            if(single.state === 'fulfilled'){
+                                result.push(single.value);
+                            }
+                        });
+                        return result;
+                    }());
+                    result.error = (function(){
+                        var result = [];
+                        infoList.forEach(function(single){
+                            if(single.state === 'rejected'){
+                                result.push(single.reason);
+                            }
+                        });
+                        return result;
+                    }());
+                    result.toUpdate = (function(){
+                        var result = [];
+                        infoList.forEach(function(single){
+                            if(single.state === 'fulfilled'){
+                                if(single.value.isDirectory){
+                                    result.push(single.value);
+                                }
+                            }
+                        });
+                        return result;
+                    }());
+                    resolve(result);
+                }, function(err){
+                    reject(err);
+                });
+            });
+        });
+
+    }
+};
\ No newline at end of file
diff --git a/lib/treenode-util.js b/lib/treenode-util.js
new file mode 100644
index 0000000..ae658ce
--- /dev/null
+++ b/lib/treenode-util.js
@@ -0,0 +1,121 @@
+/**
+ * Created by LuJian on 2016/7/4.
+ */
+
+var util = require('./options-util');
+module.exports = {
+    updateTree : function(tree, data) {
+        var self = this;
+        if (tree.data === undefined) {
+            if(data === undefined){
+                throw new Error('data is undefined');
+            }
+            tree.data = [];
+            tree.data.push(self.getFormatNode(data));
+            tree.toUpdate = data.toUpdate;
+            tree.current = data.parent;
+        } else {
+            if (tree.toUpdate.length === 0) {
+                throw new Error('should not notify data when there is no sub node to update.');
+            }
+
+            for (var i = 0; i < tree.toUpdate; i++) {
+                if (tree.toUpdate[i] === data.parent) {
+                    tree.toUpdate.splice(i, 1);
+                }
+            }
+            data.toUpdate.forEach(function (info) {
+                tree.toUpdate.push(info);
+            });
+            self.updateSubNode(tree, data);
+        }
+    },
+    getFormatNode : function(data){
+        return {name: data.parent, children: data.children.map(function(child){
+            return  {name: child.filePath, isDirectory: child.isDirectory};
+        })};
+    },
+    updateSubNode : function(tree, data) {
+        var queue = [];
+        var self = this;
+        queue.push(tree.data[0]);
+        while(queue.length > 0){
+            var current = queue.shift();
+            if(current.name === data.parent){
+                current.children = self.getFormatNode(data).children;
+                return;
+            }else{
+                if(current.children){
+                    current.children.map(function(child){
+                        queue.push(child);
+                    });
+                }
+            }
+        }
+    },
+    removeEmptyDirs : function(tree, options){
+        var self = this;
+        if(util.isOptionsFileEmpty(options.file)){
+            return;
+        }
+        var root = tree.data[0];
+        var stack = [];
+        //visit , 0 , 1  (unvisited, visited)
+        root.visit = 0;
+        root.cvisit = 0;
+        stack.push(root);
+        while(stack.length > 0){
+            var current = stack[stack.length - 1];
+            if(current.children.length === 0){
+                current.cvisit = 1;
+            }else{
+                var hasUnVisited = false;
+                for(var i = 0; i < current.children.length; i ++){
+                    if(current.children[i].isDirectory){
+                        if(!current.children[i].visit){
+                            hasUnVisited = true;
+                        }else if(current.children[i].visit === 0){
+                            hasUnVisited = true;
+                        }
+                    }
+                }
+                if(!hasUnVisited){
+                    current.cvisit = 1;
+                }
+            }
+            if(current.cvisit === 0){
+                for(var j = 0; j < current.children.length; j ++){
+                    if(current.children[j].isDirectory){
+                        current.children[j].visit = 0;
+                        current.children[j].cvisit = 0;
+                        stack.push(current.children[j]);
+                    }
+                }
+            }else{
+                if(current.children.length === 0){
+                    //visit current node
+                    self.removeTreeNode(tree, current.name);
+                }
+                current.visit = 1;
+                stack.pop();
+            }
+        }
+    },
+    removeTreeNode : function(tree, name){
+        var root = tree.data[0];
+        var queue = [];
+        queue.push({parent: {children: [root]}, real: root, index: 0});
+        while(queue.length > 0){
+            var current = queue.pop();
+            if(current.real.name === name){
+                current.parent.children.splice(current.index, 1);
+            }else{
+                for(var i = 0; i < current.real.children.length; i++){
+                    if(current.real.children[i].isDirectory){
+                        queue.push({parent: current.real, real: current.real.children[i], index: i});
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 613ce85..2f786c9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "dir-tree-scanner",
-  "version": "1.0.9",
+  "version": "1.0.13",
   "description": "directory tree scanner",
   "main": "index.js",
   "scripts": {
diff --git a/test/dir-tree.t.js b/test/dir-tree.t.js
index 6b06de1..cd2b35f 100644
--- a/test/dir-tree.t.js
+++ b/test/dir-tree.t.js
@@ -9,75 +9,6 @@ chai.should();
 var dir_tree = require('./../lib/dir-tree.js');
 
 describe('dir-tree', function(){
-  describe('#subDirectory()', function(){
-
-    var options = {
-      file: []
-    };
-
-    it('should return empty array', function(){
-      var result = [].map(function(item){return item});
-      expect(result).deep.equal([]);
-    });
-
-
-    it('should find all the sub directories', function(){
-      var fPath = path.join(__dirname, './for-test');
-      return dir_tree.subDirectory(fPath, options).should.eventually.have.then(function(subDirs){
-          expect(subDirs).have.deep.property('parent', fPath);
-          expect(subDirs).have.deep.property('children').have.length(6);
-        }, function(err){
-          expect(err).to.be.null;
-        });
-    });
-
-    it('should filter by options suffix array', function(){
-      var fPath = path.join(__dirname, './for-test');
-      var forOptions = {
-        file: ['.txt', '.js', '.md']
-      };
-      return dir_tree.subDirectory(fPath, forOptions).should.eventually.have.then(function(subDirs){
-        expect(subDirs).have.deep.property('parent', fPath);
-        expect(subDirs).have.deep.property('children').have.length(5);
-      }, function(err){
-        expect(err).to.be.null;
-      });
-    });
-
-    it('should filter by options suffix regexp', function(){
-      var fPath =path.join(__dirname, './for-test');
-      var forOptions = {
-        file: /(\.txt|\.js|\.md)$/
-      };
-      return dir_tree.subDirectory(fPath, forOptions).should.eventually.have.then(function(subDirs){
-        expect(subDirs).have.deep.property('parent', fPath);
-        expect(subDirs).have.deep.property('children').have.length(5);
-      }, function(err){
-        expect(err).to.be.null;
-      });
-    });
-
-    it('should return an empty array where input directory is empty', function(){
-      var fPath = path.join(__dirname, './for-test/b');
-      return dir_tree.subDirectory(fPath, options).should.eventually.have.then(function(subDirs){
-        expect(subDirs).have.deep.property('parent', fPath);
-        expect(subDirs).have.deep.property('children').have.length(1);
-      }, function(err){
-        expect(err).to.be.null;
-      })
-    });
-
-    it('should throw an error when input is file', function(){
-      var fPath = path.join(__dirname, './for-test/c.txt');
-      return dir_tree.subDirectory(fPath, options).should.eventually.have.then(function(subDirs){
-        expect(subDirs).to.be.null;
-      }, function(err){
-        expect(err).to.be.not.null;
-      });
-    });
-
-  });
-
   describe('for queue operations', function(){
 
   it('should act like queue', function(){
@@ -103,94 +34,6 @@ describe('dir-tree', function(){
 
 });
 
-  describe('#_checkIfDirectory', function(){
-    it('should false when input is not a directory', function(){
-      var fPath =path.join(__dirname, './for-test/c.txt');
-      return dir_tree._checkIfDirectory(fPath).should.eventually.have.then(function(data){
-        expect(data).to.be.false;
-      }, function(err){
-        expect(err).to.be.null;
-      });
-    });
-
-    it('should true when input is a directory', function(){
-      var fPath = path.join(__dirname, './for-test/a');
-      return dir_tree._checkIfDirectory(fPath).should.eventually.have.then(function(data){
-        expect(data).to.be.true;
-      }, function(err){
-        expect(err).to.be.null;
-      });
-    });
-  });
-
-  describe('#scan', function(){
-    it('should scan directory', function(){
-      var dest = path.join(__dirname, './for-test');
-      var jqTree = {};
-      return dir_tree.scan(dest).should.eventually.have.then(function(data){
-        //console.log('success----data ', jqTree);
-        expect(data).to.deep.property('message', 'success');
-      }, function(err){
-        expect(err).to.be.null;
-      }, function(data){
-        //console.log('--------------notify start --------------');
-        //console.log('parent %s, children ', data.parent, data.children, ' toUpdate: ', data.toUpdate);
-        //console.log('--------------notify end --------------');
-        updateTree(jqTree, data);
-      });
-
-      function updateTree(tree, data) {
-        if (tree.data === undefined) {
-          if(data === undefined){
-            throw new Error('data is undefined');
-            return;
-          }
-          tree.data = [];
-          tree.data.push({name: data.parent, children: data.children.map(function(child){
-            return  {name: child.filePath, isDirectory: child.isDirectory};
-          })});
-          tree.toUpdate = data.toUpdate;
-          tree.current = data.parent;
-        } else {
-          if (tree.toUpdate.length === 0) {
-            throw new Error('should not notify data when there is no sub node to update.');
-            return;
-          }
-
-          for (var i = 0; i < tree.toUpdate; i++) {
-            if (tree.toUpdate[i] === data.parent) {
-              tree.toUpdate.splice(i, 1);
-            }
-          }
-          data.toUpdate.forEach(function (info) {
-            tree.toUpdate.push(info);
-          });
-          updateSubNode(tree, data);
-        }
-
-        function updateSubNode(tree, data) {
-          var queue = [];
-          queue.push(tree.data[0]);
-          while(queue.length > 0){
-            var current = queue.shift();
-            if(current.name === data.parent){
-              current.children = data.children.map(function (child) {
-                return {name: child.filePath, isDirectory: child.isDirectory};
-              });
-              return;
-            }else{
-              if(current.children){
-                current.children.forEach(function(child){
-                  queue.push(child);
-                });
-              }
-            }
-          }
-        }
-      }
-    });
-  });
-
   describe('#find()', function(){
 
     it('should find recursively', function(){
@@ -239,38 +82,5 @@ describe('dir-tree', function(){
 
   });
 
-  describe('#remove tree node()', function(){
-
-    it('should remove tree node when the value exists', function(){
-
-      var tree = {data: [{
-        name: 'a',
-        children: [
-          {name: 'b', isDirectory: true, children: [{name: 'b1', children: []}]},
-          {name: 'c', isDirectory: true, children: [{name: 'c1', children: []}]},
-          {name: 'd', isDirectory: true, children: [{name: 'd1', children: []}]}
-        ]
-      }]};
-
-      dir_tree.removeTreeNode(tree, 'b');
-      expect(tree).to.have.deep.property('data[0].children').to.length(2);
-    });
-
-    it('should not remove any tree node when the value doesn\'t exist', function(){
-      var tree = {data: [{
-        name: 'a',
-        children: [
-          {name: 'b', isDirectory: true, children: [{name: 'b1', children: []}]},
-          {name: 'c', isDirectory: true, children: [{name: 'c1', children: []}]},
-          {name: 'd', isDirectory: true, children: [{name: 'd1', children: []}]}
-        ]
-      }]};
-
-      dir_tree.removeTreeNode(tree, 'e');
-      expect(tree).to.have.deep.property('data[0].children').to.length(3);
-    });
-
-  });
-
 });
 
diff --git a/test/file-stat.t.js b/test/file-stat.t.js
new file mode 100644
index 0000000..a44d273
--- /dev/null
+++ b/test/file-stat.t.js
@@ -0,0 +1,77 @@
+/**
+ * Created by LuJian on 2016/6/28.
+ */
+var chai  = require('chai');
+var expect = chai.expect;
+var chaiAsPromised = require('chai-as-promised');
+var path = require('path');
+chai.use(chaiAsPromised);
+chai.should();
+
+var fileStat = require('./../lib/file-stat');
+
+describe('file-stat', function(){
+
+    describe('#fileInfo', function(){
+
+        it('should throw an error when input is empty', function(){
+            var fPath = path.join(__dirname, './for-test-1');
+            return fileStat.fileInfo(fPath).should.eventually.have
+                .then(function(data){
+                    expect(data).to.be.null;
+                }, function(err){
+                    expect(err).to.be.have.deep.property('code', 'ENOENT');
+                });
+        });
+
+        it('should act as expect show the file info when input is exist and input is directory', function(){
+            var fPath = path.join(__dirname, './for-test');
+            return fileStat.fileInfo(fPath).should.eventually.have
+                .then(function(data){
+                    expect(data).to.be.not.null;
+                    expect(data).to.have.deep.property('filePath', fPath);
+                    expect(data).to.have.deep.property('isDirectory', true);
+                }, function(err){
+                    expect(err).to.be.null;
+                });
+        });
+
+        it('should act as expect show the file info when input is exist and input is file', function(){
+            var fPath = path.join(__dirname, './for-test', './c.txt');
+            return fileStat.fileInfo(fPath).should.eventually.have
+                .then(function(data){
+                    expect(data).to.be.not.null;
+                    expect(data).to.have.deep.property('filePath', fPath);
+                    expect(data).to.have.deep.property('isDirectory', false);
+                }, function(err){
+                    expect(err).to.be.null;
+                });
+        });
+
+        /**
+         * this test case contains permission problem
+         * test pass
+         */
+        // it('should throw an error when input has no permission', function(){
+        //     var fPath = path.join(__dirname, './for-test-no-permission');
+        //     return fileStat.fileInfo(fPath).should.eventually.have
+        //         .then(function(data){
+        //             expect(data).to.be.null;
+        //         }, function(err){
+        //             expect(err).to.have.deep.property('code', 'EPERM');
+        //         });
+        // });
+
+        it('should return is directory', function(){
+            var fPath = path.join(__dirname, './for-test');
+            return fileStat.isDirectory(fPath).should.eventually.have
+                .then(function(data){
+                    expect(data).to.be.true;
+                }, function(err){
+                    expect(err).to.be.null;
+                });
+        });
+
+    });
+
+});
\ No newline at end of file
diff --git a/test/options-util.t.js b/test/options-util.t.js
new file mode 100644
index 0000000..5efd794
--- /dev/null
+++ b/test/options-util.t.js
@@ -0,0 +1,90 @@
+/**
+ * Created by LuJian on 2016/7/4.
+ */
+var chai  = require('chai');
+var expect = chai.expect;
+var chaiAsPromised = require('chai-as-promised');
+var path = require('path');
+chai.use(chaiAsPromised);
+chai.should();
+
+var util = require('./../lib/options-util');
+
+describe('options-util', function(){
+
+    describe('#filterFileType', function(){
+
+        it('should throw an error when option has not file', function(){
+            try{
+                util.filterFileType(null, null);
+            }catch(err){
+                expect(err).to.be.not.null;
+            }
+        });
+
+        it('should do nothing when file is not regular format', function(){
+           util.filterFileType('xx/abx', { file : ['.js']}).should.to.be.true;
+        });
+
+        it('should do nothing when file option is empty array', function(){
+           util.filterFileType('xx/abx.js', { file : []}).should.to.be.true;
+        });
+
+        it('should do filtering with true when file option contains filter content', function(){
+           util.filterFileType('xx/abx.js', { file : ['.js']}).should.to.be.true;
+        });
+
+        it('should do filtering with false when file option contains filter content', function(){
+           util.filterFileType('xx/abx.md', { file : ['.js']}).should.to.be.false;
+        });
+
+        it('should do filtering with true when file option contains multiple filtering content', function(){
+            util.filterFileType('xx/abx.js', { file : ['.md', '.css', '.js']}).should.to.be.true;
+        });
+
+        it('should do filtering with true when file reg exp', function(){
+           util.filterFileType('xx/abx.js', { file : /\.js$/}).should.to.be.true;
+        });
+
+        it('should do filtering with true when file reg exp contains multiple filtering content', function(){
+           util.filterFileType('xx/abx.js', { file : /\.md|css|mst|js$/}).should.to.be.true;
+        });
+
+        it('should do filtering with false when file reg exp', function(){
+           util.filterFileType('xx/abx.js', { file : /\.md$/}).should.to.be.false;
+        });
+
+    });
+
+    describe('#isOptionsFileEmpty', function(){
+
+        it('should return true when options undefined', function(){
+            util.isOptionsFileEmpty(undefined).should.be.true;
+            util.isOptionsFileEmpty(null).should.be.true;
+        })
+
+        it('should return true when options file array length is zero', function(){
+            util.isOptionsFileEmpty([]).should.be.true;
+        })
+
+        it('should return false when options file array is not empty', function(){
+            util.isOptionsFileEmpty(['.js']).should.be.false;
+        })
+
+    })
+
+    describe('#getDefaultOptions', function(){
+
+        it('should return the default options', function(){
+            util.getDefaultOption(null).should.have.deep.property('file')
+                .deep.equal([]);
+        })
+
+        it('should return real options when options is set', function(){
+            util.getDefaultOption({ file : [1]}).should.have.deep.property('file')
+                .deep.equal([1]);
+        })
+
+    })
+
+});
\ No newline at end of file
diff --git a/test/sub-files.t.js b/test/sub-files.t.js
new file mode 100644
index 0000000..851ac5b
--- /dev/null
+++ b/test/sub-files.t.js
@@ -0,0 +1,77 @@
+/**
+ * Created by LuJian on 2016/6/28.
+ */
+var chai  = require('chai');
+var expect = chai.expect;
+var chaiAsPromised = require('chai-as-promised');
+var path = require('path');
+chai.use(chaiAsPromised);
+chai.should();
+
+var subFiles = require('./../lib/sub-files');
+
+describe('sub-files', function(){
+
+    describe('#fileDirectSubFiles', function(){
+
+        it('should list all sub file when input is directory', function(){
+            var fPath = path.join(__dirname, './for-test');
+            return subFiles.findDirectSubFiles(fPath, { file : []}).should.eventually.have
+                .then(function(data){
+                    expect(data).to.have.deep.property('children').length(6);
+                }, function(err){
+                    expect(err).to.be.null;
+                });
+        })
+
+        it('should throw an error when input is file', function(){
+            var fPath = path.join(__dirname, './for-test', './c.txt');
+            return subFiles.findDirectSubFiles(fPath, { file : []}).should.eventually.have
+                .then(function(data){
+                    expect(data).to.be.null;
+                }, function(err){
+                   expect(err).to.have.deep.property('code', 'ENOTDIR');
+                });
+        })
+
+        it('should list sub files when input contains no access right file', function(){
+            var fPath = path.join(__dirname, './');
+            return subFiles.findDirectSubFiles(fPath, { file : []}).should.eventually.have
+                .then(function(data){
+                    expect(data).to.be.not.null;
+                }, function(err){
+                   expect(err).to.be.null;
+                });
+        })
+
+        /**
+         * this test case involves permission problem
+         */
+        // it('should throw an error when input is no access right file', function(){
+        //    var fPath = path.join(__dirname, './for-test-no-permission');
+        //     return subFiles.findDirectSubFiles(fPath, { file : []}).should.eventually.have
+        //         .then(function(data){
+        //             expect(data).to.be.null;
+        //         }, function(err){
+        //             expect(err).to.be.not.null;
+        //         });
+        // })
+
+        /**
+         * this error is due to the window's system volume information
+         * has no enough access right problem
+         */
+        // it('should with no problem', function(){
+        //     var fPath = 'e:\\';
+        //     return subFiles.findDirectSubFiles(fPath, { file : []}).should.eventually.have
+        //         .then(function(data){
+        //             expect(data).to.be.not.null;
+        //         }, function(err){
+        //             console.log(err)
+        //            expect(err).to.be.null;
+        //         });
+        // })
+
+    });
+
+});
\ No newline at end of file
diff --git a/test/test.x.js b/test/test.x.js
new file mode 100644
index 0000000..f72c259
--- /dev/null
+++ b/test/test.x.js
@@ -0,0 +1,13 @@
+/**
+ * Created by LuJian on 2016/7/5.
+ */
+
+
+var dirTree = require('./../lib/dir-tree');
+dirTree.find('e://').then(function(data){
+    dirTree.display(data.data);
+}).fail(function(data){
+    console.log(data);
+}).progress(function(data){
+    console.log(data);
+})
\ No newline at end of file
diff --git a/test/treenode-util.t.js b/test/treenode-util.t.js
new file mode 100644
index 0000000..6f1c098
--- /dev/null
+++ b/test/treenode-util.t.js
@@ -0,0 +1,111 @@
+/**
+ * Created by LuJian on 2016/7/4.
+ */
+var chai  = require('chai');
+var expect = chai.expect;
+var chaiAsPromised = require('chai-as-promised');
+var path = require('path');
+chai.use(chaiAsPromised);
+chai.should();
+
+var treeUtil = require('./../lib/treenode-util');
+
+describe('tree-node-util', function(){
+
+
+    describe('#updateTree', function(){
+
+        it('should update tree and remove update list', function(){
+
+            var tree = {data: [{
+                name: 'a',
+                children: [
+                    {name: 'b', isDirectory: true, children: [{name: 'b1', isDirectory: true, children: []}]},
+                    {name: 'c', isDirectory: true, children: [{name: 'c1', isDirectory: true, children: []}]},
+                    {name: 'd', isDirectory: true, children: [{name: 'd1', isDirectory: true, children: []}]}
+                ]
+            }],
+            toUpdate: [{name: 'b1'}]};
+
+            var data = {
+                parent : 'b1',
+                children: [
+                    {filePath: 'b11', isDirectory: true},
+                    {filePath: 'b12', isDirectory: false},
+                    {filePath: 'b13', isDirectory: false}
+                ],
+                toUpdate: []
+            }
+
+            treeUtil.updateTree(tree, data);
+            expect(tree).to.have.deep.property('data[0].children[0].children[0].children').length(3);
+        })
+
+    })
+
+    describe('#removeEmptyDirs', function(){
+
+        it('should do nothing when options file is empty', function(){
+            var tree = {data: [{
+                name: 'a',
+                children: [
+                    {name: 'b', isDirectory: true, children: [{name: 'b1', isDirectory: true, children: []}]},
+                    {name: 'c', isDirectory: true, children: [{name: 'c1', isDirectory: true, children: []}]},
+                    {name: 'd', isDirectory: true, children: [{name: 'd1', isDirectory: true, children: []}]}
+                ]
+            }]};
+
+            treeUtil.removeEmptyDirs(tree, { file: []});
+            expect(tree).to.be.deep.property('data[0].children').length(3);
+        })
+
+        it('should remove empty dir', function(){
+
+            var tree = {data: [{
+                name: 'a',
+                children: [
+                    {name: 'b', isDirectory: true, children: [{name: 'b1', isDirectory: true, children: []}]},
+                    {name: 'c', isDirectory: true, children: [{name: 'c1.js', isDirectory: false, children: []}]},
+                    {name: 'd', isDirectory: true, children: [{name: 'd1.js', isDirectory: false, children: []}]}
+                ]
+            }]};
+
+            treeUtil.removeEmptyDirs(tree, { file: ['.js']});
+            expect(tree).to.be.deep.property('data[0].children').length(2);
+
+        })
+
+    })
+
+    describe('#removeTreeNode', function(){
+        it('should remove tree node when the value exists', function(){
+
+            var tree = {data: [{
+                name: 'a',
+                children: [
+                    {name: 'c', isDirectory: true, children: [{name: 'c1', children: []}]},
+                    {name: 'b', isDirectory: true, children: [{name: 'b1', children: []}]},
+                    {name: 'd', isDirectory: true, children: [{name: 'd1', children: []}]}
+                ]
+            }]};
+
+            treeUtil.removeTreeNode(tree, 'b');
+            expect(tree).to.have.deep.property('data[0].children').to.length(2);
+        });
+
+        it('should not remove any tree node when the value doesn\'t exist', function(){
+            var tree = {data: [{
+                name: 'a',
+                children: [
+                    {name: 'b', isDirectory: true, children: [{name: 'b1', children: []}]},
+                    {name: 'c', isDirectory: true, children: [{name: 'c1', children: []}]},
+                    {name: 'd', isDirectory: true, children: [{name: 'd1', children: []}]}
+                ]
+            }]};
+
+            treeUtil.removeTreeNode(tree, 'e');
+            expect(tree).to.have.deep.property('data[0].children').to.length(3);
+        });
+    })
+
+})
\ No newline at end of file
