Browse Source

Code refactor

Ben Hsieh 8 years ago
parent
commit
2cbb8f7d5b

+ 12
- 0
src/class/RNFetchBlobFile.js View File

@@ -0,0 +1,12 @@
1
+import {
2
+  NativeModules,
3
+  DeviceEventEmitter,
4
+  NativeAppEventEmitter,
5
+} from 'react-native'
6
+
7
+const RNFetchBlob = NativeModules.RNFetchBlob
8
+const emitter = DeviceEventEmitter
9
+
10
+export default class RNFetchBlobFile {
11
+
12
+}

+ 76
- 0
src/class/RNFetchBlobReadStream.js View File

@@ -0,0 +1,76 @@
1
+import {
2
+  NativeModules,
3
+  DeviceEventEmitter,
4
+  NativeAppEventEmitter,
5
+} from 'react-native'
6
+
7
+const RNFetchBlob = NativeModules.RNFetchBlob
8
+const emitter = DeviceEventEmitter
9
+
10
+export default class RNFetchBlobReadStream {
11
+
12
+  path : string;
13
+  encoding : 'utf8' | 'ascii' | 'base64';
14
+  bufferSize : ?number;
15
+  closed : boolean;
16
+
17
+  constructor(path:string, encoding:string, bufferSize?:?number) {
18
+    if(!path)
19
+      throw Error('RNFetchBlob could not open file stream with empty `path`')
20
+    this.encoding = encoding || 'utf8'
21
+    this.bufferSize = bufferSize
22
+    this.path = path
23
+    this.closed = false
24
+    this._onData = () => {}
25
+    this._onEnd = () => {}
26
+    this._onError = () => {}
27
+
28
+    // register for file stream event
29
+    let subscription = emitter.addListener(`RNFetchBlobStream+${this.path}`, (e) => {
30
+    
31
+      let {event, detail} = e
32
+      if(this._onData && event === 'data')
33
+        this._onData(detail)
34
+      else if (this._onEnd && event === 'end') {
35
+        this._onEnd(detail)
36
+      }
37
+      else {
38
+        if(this._onError)
39
+          this._onError(detail)
40
+        else
41
+          throw new Error(detail)
42
+      }
43
+      // when stream closed or error, remove event handler
44
+      if (event === 'error' || event === 'end') {
45
+        subscription.remove()
46
+        this.closed = true
47
+      }
48
+    })
49
+
50
+  }
51
+
52
+  open() {
53
+    if(!this.closed)
54
+      RNFetchBlob.readStream(this.path, this.encoding, this.bufferSize || 0)
55
+    else
56
+      throw new Error('Stream closed')
57
+  }
58
+
59
+  onData(fn) {
60
+    if(this.encoding.toLowerCase() === 'ascii')
61
+      this._onData = (data) => {
62
+        fn(JSON.parse(data))
63
+      }
64
+    else
65
+      this._onData = fn
66
+  }
67
+
68
+  onError(fn) {
69
+    this._onError = fn
70
+  }
71
+
72
+  onEnd (fn) {
73
+    this._onEnd = fn
74
+  }
75
+
76
+}

+ 66
- 0
src/class/RNFetchBlobSession.js View File

@@ -0,0 +1,66 @@
1
+/**
2
+ * Session class
3
+ * @class RNFetchBlobSession
4
+ */
5
+
6
+import {
7
+ NativeModules,
8
+ DeviceEventEmitter,
9
+ NativeAppEventEmitter,
10
+} from 'react-native'
11
+
12
+const RNFetchBlob = NativeModules.RNFetchBlob
13
+const emitter = DeviceEventEmitter
14
+
15
+export default class RNFetchBlobSession {
16
+
17
+  add : (path:string) => RNFetchBlobSession;
18
+  remove : (path:string) => RNFetchBlobSession;
19
+  dispose : () => Promise;
20
+  list : () => Array<string>;
21
+  name : string;
22
+
23
+  constructor(name:string, list:Array<string>) {
24
+    this.name = name
25
+    if(!sessions[name]) {
26
+      if(Array.isArray(list))
27
+      sessions[name] = list
28
+      else
29
+      sessions[name] = []
30
+    }
31
+  }
32
+
33
+  add(path:string):RNFetchBlobSession {
34
+    sessions[this.name].push(path)
35
+    return this
36
+  }
37
+
38
+  remove(path:string):RNFetchBlobSession {
39
+    let list = sessions[this.name]
40
+    for(let i in list) {
41
+      if(list[i] === path) {
42
+        sessions[this.name].splice(i, 1)
43
+        break;
44
+      }
45
+    }
46
+    return this
47
+  }
48
+
49
+  list():Array<string> {
50
+    return sessions[this.name]
51
+  }
52
+
53
+  dispose():Promise {
54
+    return new Promise((resolve, reject) => {
55
+      RNFetchBlob.removeSession(sessions[this.name], (err) => {
56
+        if(err)
57
+          reject(err)
58
+        else {
59
+          delete sessions[this.name]
60
+          resolve()
61
+        }
62
+      })
63
+    })
64
+  }
65
+
66
+}

+ 54
- 0
src/class/RNFetchBlobWriteStream.js View File

@@ -0,0 +1,54 @@
1
+import {
2
+ NativeModules,
3
+ DeviceEventEmitter,
4
+ NativeAppEventEmitter,
5
+} from 'react-native'
6
+
7
+const RNFetchBlob = NativeModules.RNFetchBlob
8
+const emitter = DeviceEventEmitter
9
+
10
+export default class RNFetchBlobWriteStream {
11
+
12
+  id : string;
13
+  encoding : string;
14
+  append : bool;
15
+
16
+  constructor(streamId:string, encoding:string, append:string) {
17
+    this.id = streamId
18
+    this.encoding = encoding
19
+    this.append = append
20
+  }
21
+
22
+  write(data:string) {
23
+    return new Promise((resolve, reject) => {
24
+      try {
25
+        let method = this.encoding === 'ascii' ? 'writeArrayChunk' : 'writeChunk'
26
+        if(this.encoding.toLocaleLowerCase() === 'ascii' && !Array.isArray(data)) {
27
+            reject('ascii input data must be an Array')
28
+            return
29
+        }
30
+        RNFetchBlob[method](this.id, data, (error) => {
31
+          if(error)
32
+            reject(error)
33
+          else
34
+            resolve()
35
+        })
36
+      } catch(err) {
37
+        reject(err)
38
+      }
39
+    })
40
+  }
41
+
42
+  close() {
43
+    return new Promise((resolve, reject) => {
44
+      try {
45
+        RNFetchBlob.closeStream(this.id, () => {
46
+          resolve()
47
+        })
48
+      } catch (err) {
49
+        reject(err)
50
+      }
51
+    })
52
+  }
53
+
54
+}

+ 36
- 154
src/fs.js View File

@@ -10,6 +10,10 @@ import {
10 10
   DeviceEventEmitter,
11 11
   NativeAppEventEmitter,
12 12
 } from 'react-native'
13
+import RNFetchBlobSession from './class/RNFetchBlobSession'
14
+import RNFetchBlobWriteStream from './class/RNFetchBlobWriteStream'
15
+import RNFetchBlobReadStream from './class/RNFetchBlobReadStream'
16
+import RNFetchBlobFile from './class/RNFetchBlobFile'
13 17
 import type {
14 18
   RNFetchBlobNative,
15 19
   RNFetchBlobConfig,
@@ -87,16 +91,17 @@ function writeStream(
87 91
   path : string,
88 92
   encoding : 'utf8' | 'ascii' | 'base64',
89 93
   append? : ?bool,
90
-):Promise<WriteStream> {
94
+):Promise<RNFetchBlobWriteStream> {
91 95
   if(!path)
92 96
     throw Error('RNFetchBlob could not open file stream with empty `path`')
93
-
97
+  encoding = encoding || 'utf8'
98
+  append = append || false
94 99
   return new Promise((resolve, reject) => {
95 100
     RNFetchBlob.writeStream(path, encoding || 'base64', append || false, (err, streamId:string) => {
96 101
       if(err)
97 102
         reject(err)
98 103
       else
99
-        resolve(new WriteStream(streamId, encoding))
104
+        resolve(new RNFetchBlobWriteStream(streamId, encoding))
100 105
     })
101 106
   })
102 107
 }
@@ -112,55 +117,15 @@ function readStream(
112 117
   path : string,
113 118
   encoding : 'utf8' | 'ascii' | 'base64',
114 119
   bufferSize? : ?number
115
-):RNFetchBlobStream {
116
-
117
-  if(!path)
118
-    throw Error('RNFetchBlob could not open file stream with empty `path`')
119
-  encoding = encoding || 'utf8'
120
-  let stream:RNFetchBlobStream = {
121
-    // parse JSON array when encoding is ASCII
122
-    onData : function(fn) {
123
-      if(encoding.toLowerCase() === 'ascii')
124
-        this._onData = (data) => {
125
-          fn(JSON.parse(data))
126
-        }
127
-      else
128
-        this._onData = fn
129
-    },
130
-    onError : function(fn) {
131
-      this._onError = fn
132
-    },
133
-    onEnd : function(fn) {
134
-      this._onEnd = fn
135
-    },
136
-  }
137
-
138
-  // register for file stream event
139
-  let subscription = emitter.addListener(`RNFetchBlobStream+${path}`, (e) => {
140
-
141
-    let {event, detail} = e
142
-    if(stream._onData && event === 'data')
143
-      stream._onData(detail)
144
-    else if (stream._onEnd && event === 'end') {
145
-      stream._onEnd(detail)
146
-    }
147
-    else {
148
-      if(stream._onError)
149
-        stream._onError(detail)
150
-      else
151
-        throw new Error(detail)
152
-    }
153
-    // when stream closed or error, remove event handler
154
-    if (event === 'error' || event === 'end') {
155
-      subscription.remove()
156
-    }
157
-  })
158
-
159
-  RNFetchBlob.readStream(path, encoding, bufferSize || 0)
160
-  return stream
161
-
120
+):Promise<RNFetchBlobReadStream> {
121
+  return Promise.resolve(new RNFetchBlobReadStream(path, encoding, bufferSize))
162 122
 }
163 123
 
124
+/**
125
+ * Create a directory.
126
+ * @param  {string} path Path of directory to be created
127
+ * @return {Promise}
128
+ */
164 129
 function mkdir(path:string):Promise {
165 130
 
166 131
   return new Promise((resolve, reject) => {
@@ -174,6 +139,27 @@ function mkdir(path:string):Promise {
174 139
 
175 140
 }
176 141
 
142
+/**
143
+ * Show statistic data of a path.
144
+ * @param  {string} path Target path
145
+ * @return {RNFetchBlobFile}
146
+ */
147
+function stat(path:string):Promise<RNFetchBlobFile> {
148
+
149
+}
150
+
151
+/**
152
+ * Android only method, request media scanner to scan the file.
153
+ * @param  {Array<string>} paths Paths of files to be scanned.
154
+ * @param  {Array<string>} mimes Optional array of MIME types for each path.
155
+ *                               If mimeType is null, then the mimeType will be
156
+ *                               inferred from the file extension.
157
+ * @return {Promise}
158
+ */
159
+function scanFile(paths:Array<string>, mimes: Array<string>):Promise {
160
+
161
+}
162
+
177 163
 function cp(path:string, dest:string):Promise<boolean> {
178 164
   return new Promise((resolve, reject) => {
179 165
     RNFetchBlob.cp(path, dest, (err, res) => {
@@ -256,110 +242,6 @@ function isDir(path:string):Promise<bool, bool> {
256 242
 
257 243
 }
258 244
 
259
-
260
-/**
261
- * Session class
262
- * @class RNFetchBlobSession
263
- */
264
-class RNFetchBlobSession {
265
-
266
-  add : (path:string) => RNFetchBlobSession;
267
-  remove : (path:string) => RNFetchBlobSession;
268
-  dispose : () => Promise;
269
-  list : () => Array<string>;
270
-  name : string;
271
-
272
-  constructor(name:string, list:Array<string>) {
273
-    this.name = name
274
-    if(!sessions[name]) {
275
-      if(Array.isArray(list))
276
-      sessions[name] = list
277
-      else
278
-      sessions[name] = []
279
-    }
280
-  }
281
-
282
-  add(path:string):RNFetchBlobSession {
283
-    sessions[this.name].push(path)
284
-    return this
285
-  }
286
-
287
-  remove(path:string):RNFetchBlobSession {
288
-    let list = sessions[this.name]
289
-    for(let i in list) {
290
-      if(list[i] === path) {
291
-        sessions[this.name].splice(i, 1)
292
-        break;
293
-      }
294
-    }
295
-    return this
296
-  }
297
-
298
-  list():Array<string> {
299
-    return sessions[this.name]
300
-  }
301
-
302
-  dispose():Promise {
303
-    return new Promise((resolve, reject) => {
304
-      RNFetchBlob.removeSession(sessions[this.name], (err) => {
305
-        if(err)
306
-          reject(err)
307
-        else {
308
-          delete sessions[this.name]
309
-          resolve()
310
-        }
311
-      })
312
-    })
313
-  }
314
-
315
-}
316
-
317
-class WriteStream {
318
-
319
-  id : string;
320
-  encoding : string;
321
-  append : bool;
322
-
323
-  constructor(streamId:string, encoding:string, append:string) {
324
-    this.id = streamId
325
-    this.encoding = encoding
326
-    this.append = append
327
-  }
328
-
329
-  write(data:string) {
330
-    return new Promise((resolve, reject) => {
331
-      try {
332
-        let method = this.encoding === 'ascii' ? 'writeArrayChunk' : 'writeChunk'
333
-        if(this.encoding.toLocaleLowerCase() === 'ascii' && !Array.isArray(data)) {
334
-            reject('ascii input data must be an Array')
335
-            return
336
-        }
337
-        RNFetchBlob[method](this.id, data, (error) => {
338
-          if(error)
339
-            reject(error)
340
-          else
341
-            resolve()
342
-        })
343
-      } catch(err) {
344
-        reject(err)
345
-      }
346
-    })
347
-  }
348
-
349
-  close() {
350
-    return new Promise((resolve, reject) => {
351
-      try {
352
-        RNFetchBlob.closeStream(this.id, () => {
353
-          resolve()
354
-        })
355
-      } catch (err) {
356
-        reject(err)
357
-      }
358
-    })
359
-  }
360
-
361
-}
362
-
363 245
 export default {
364 246
   RNFetchBlobSession,
365 247
   unlink,

+ 33
- 27
test/test-0.5.x.js View File

@@ -47,37 +47,43 @@ describe('Download file to storage with custom file extension', (report, done) =
47 47
 
48 48
 describe('Read cached file via file stream', (report, done) => {
49 49
   let data = 'data:image/png;base64, '
50
-  let stream = fs.readStream(tmpFilePath, 'base64')
51
-  stream.onData((chunk) => {
52
-    data += chunk
53
-  })
54
-  stream.onEnd(() => {
55
-    report(
56
-      <Assert key="image should have value"
57
-        expect={0}
58
-        comparer={Comparer.smaller}
59
-        actual={data.length}/>,
60
-      <Info key="image from read stream">
61
-        <Image source={{uri : data}} style={styles.image}/>
62
-      </Info>)
63
-    done()
64
-  })
65
-  stream.onError((err) => {
66
-    console.log('stream err', err)
67
-  })
50
+  fs.readStream(tmpFilePath, 'base64')
51
+    .then((stream) => {
52
+      stream.open()
53
+      stream.onData((chunk) => {
54
+        data += chunk
55
+      })
56
+      stream.onEnd(() => {
57
+        report(
58
+          <Assert key="image should have value"
59
+            expect={0}
60
+            comparer={Comparer.smaller}
61
+            actual={data.length}/>,
62
+          <Info key="image from read stream">
63
+            <Image source={{uri : data}} style={styles.image}/>
64
+          </Info>)
65
+        done()
66
+      })
67
+      stream.onError((err) => {
68
+        console.log('stream err', err)
69
+      })
70
+    })
68 71
 })
69 72
 
70 73
 describe('File stream reader error should be able to handled', (report, done) => {
71
-  let stream = fs.readStream('^_^ not exists', 'base64')
72
-  stream.onError((err) => {
73
-    report(<Info key="error message">
74
-      <Text>
75
-        {err}
76
-      </Text>
77
-    </Info>)
78
-    done()
74
+  fs.readStream('^_^ not exists', 'base64')
75
+    .then((stream) => {
76
+      stream.open()
77
+      stream.onError((err) => {
78
+        report(<Info key="error message">
79
+          <Text>
80
+            {err}
81
+          </Text>
82
+        </Info>)
83
+        done()
79 84
 
80
-  })
85
+      })
86
+    })
81 87
 })
82 88
 
83 89
 let localFile = null

+ 110
- 92
test/test-fs.js View File

@@ -77,51 +77,34 @@ describe('create file API test', (report, done) => {
77 77
 
78 78
   fs.createFile(p, raw, 'utf8')
79 79
     .then(() => {
80
-      let stream = fs.readStream(p, 'utf8')
81 80
       let d = ''
82
-      stream.onData((chunk) => {
83
-        d += chunk
84
-      })
85
-      stream.onEnd(() => {
86
-        report(<Assert key="utf8 content test"  expect={raw} actual={d}/>)
87
-        testBase64()
88
-      })
89
-    })
90
-  function testBase64() {
91
-    fs.createFile(p + '-base64', RNFetchBlob.base64.encode(raw), 'base64')
92
-      .then(() => {
93
-        let stream = fs.readStream(p + '-base64', 'utf8')
94
-        let d = ''
81
+      fs.readStream(p, 'utf8').then((stream) => {
82
+        stream.open()
95 83
         stream.onData((chunk) => {
96 84
           d += chunk
97 85
         })
98 86
         stream.onEnd(() => {
99
-          report(<Assert
100
-            key="base64 content test"
101
-            expect={raw}
102
-            actual={d}/>)
103
-          // testASCII()
104
-          done()
87
+          report(<Assert key="utf8 content test"  expect={raw} actual={d}/>)
88
+          testBase64()
105 89
         })
106 90
       })
107
-      .catch((err) => {
108
-        console.log(err)
109
-      })
110
-  }
111
-  function testASCII() {
112
-    fs.createFile(p + '-ascii', raw, 'ascii')
91
+    })
92
+  function testBase64() {
93
+    fs.createFile(p + '-base64', RNFetchBlob.base64.encode(raw), 'base64')
113 94
       .then(() => {
114
-        let stream = fs.readStream(p + '-ascii', 'ascii')
115
-        let d = ''
116
-        stream.onData((chunk) => {
117
-          d += chunk
118
-        })
119
-        stream.onEnd(() => {
120
-          report(<Assert
121
-            key="ASCII content test"
122
-            expect={raw}
123
-            actual={d}/>)
124
-          done()
95
+        fs.readStream(p + '-base64', 'utf8').then((stream) => {
96
+            stream.open()
97
+            let d = ''
98
+            stream.onData((chunk) => {
99
+              d += chunk
100
+            })
101
+            stream.onEnd(() => {
102
+              report(<Assert
103
+                key="base64 content test"
104
+                expect={raw}
105
+                actual={d}/>)
106
+                done()
107
+              })
125 108
         })
126 109
       })
127 110
       .catch((err) => {
@@ -197,17 +180,19 @@ describe('write stream API test', (report, done) => {
197 180
       return ws.close()
198 181
     })
199 182
     .then(() => {
200
-      let rs = fs.readStream(p, 'utf8')
201 183
       let d1 = ''
202
-      rs.onData((chunk) => {
203
-        d1 += chunk
204
-      })
205
-      rs.onEnd(() => {
206
-        report(
207
-          <Assert key="write data async test"
208
-            expect={'123456789011121314'}
209
-            actual={d1}/>)
210
-          base64Test()
184
+      fs.readStream(p, 'utf8').then((stream) => {
185
+        stream.open()
186
+        stream.onData((chunk) => {
187
+          d1 += chunk
188
+        })
189
+        stream.onEnd(() => {
190
+          report(
191
+            <Assert key="write data async test"
192
+              expect={'123456789011121314'}
193
+              actual={d1}/>)
194
+            base64Test()
195
+        })
211 196
       })
212 197
     })
213 198
   function base64Test() {
@@ -220,12 +205,15 @@ describe('write stream API test', (report, done) => {
220 205
       return ws.close()
221 206
     })
222 207
     .then(() => {
223
-      let rs = fs.readStream(p, 'base64')
208
+      return fs.readStream(p, 'base64')
209
+    })
210
+    .then((stream) => {
224 211
       let d2 = ''
225
-      rs.onData((chunk) => {
212
+      stream.open()
213
+      stream.onData((chunk) => {
226 214
         d2 += chunk
227 215
       })
228
-      rs.onEnd(() => {
216
+      stream.onEnd(() => {
229 217
         report(
230 218
           <Assert key="file should be overwritten by base64 encoded data"
231 219
             expect={RNFetchBlob.base64.encode(expect)}
@@ -254,14 +242,16 @@ describe('mv API test', {timeout : 10000},(report, done) => {
254 242
   })
255 243
   .then((files) => {
256 244
     report(<Assert key="file name should be correct" expect={'moved'} actual={files[0]}/>)
257
-    let rs = fs.readStream(dest + '/moved')
258
-    let actual = ''
259
-    rs.onData((chunk) => {
260
-      actual += chunk
261
-    })
262
-    rs.onEnd(() => {
263
-      report(<Assert key="file content should be correct" expect={content} actual={actual}/>)
264
-      done()
245
+    fs.readStream(dest + '/moved').then((rs) => {
246
+      rs.open()
247
+      let actual = ''
248
+      rs.onData((chunk) => {
249
+        actual += chunk
250
+      })
251
+      rs.onEnd(() => {
252
+        report(<Assert key="file content should be correct" expect={content} actual={actual}/>)
253
+        done()
254
+      })
265 255
     })
266 256
   })
267 257
 })
@@ -280,14 +270,16 @@ describe('cp API test', {timeout : 10000},(report, done) => {
280 270
   })
281 271
   .then((files) => {
282 272
     report(<Assert key="file name should be correct" expect={'cp'} actual={files[0]}/>)
283
-    let rs = fs.readStream(dest + '/cp')
284
-    let actual = ''
285
-    rs.onData((chunk) => {
286
-      actual += chunk
287
-    })
288
-    rs.onEnd(() => {
289
-      report(<Assert key="file content should be correct" expect={content} actual={actual}/>)
290
-      done()
273
+    fs.readStream(dest + '/cp').then((rs) => {
274
+      rs.open()
275
+      let actual = ''
276
+      rs.onData((chunk) => {
277
+        actual += chunk
278
+      })
279
+      rs.onEnd(() => {
280
+        report(<Assert key="file content should be correct" expect={content} actual={actual}/>)
281
+        done()
282
+      })
291 283
     })
292 284
   })
293 285
 })
@@ -304,30 +296,30 @@ describe('ASCII data test', (report, done) => {
304 296
       return fs.writeStream(p, 'ascii', false)
305 297
     })
306 298
     .then((ofstream) => {
307
-      let qq = []
308 299
       for(let i=0;i<expect.length;i++) {
309
-        qq.push(expect[i].charCodeAt(0))
310 300
         ofstream.write([expect[i].charCodeAt(0)])
311 301
       }
312 302
       ofstream.write(['g'.charCodeAt(0), 'g'.charCodeAt(0)])
313 303
       return ofstream.close()
314 304
     })
315 305
     .then(() => {
316
-      let ifstream = fs.readStream(p, 'ascii')
317
-      let res = []
318
-      ifstream.onData((chunk) => {
319
-        res = res.concat(chunk)
320
-      })
321
-      ifstream.onEnd(() => {
322
-        res = res.map((byte) => {
323
-          return String.fromCharCode(byte)
324
-        }).join('')
325
-        report(
326
-          <Assert key="data written in ASCII format should correct"
327
-            expect={expect + 'gg'}
328
-            actual={res}
329
-          />)
330
-        done()
306
+      fs.readStream(p, 'ascii').then((ifstream) => {
307
+        let res = []
308
+        ifstream.open()
309
+        ifstream.onData((chunk) => {
310
+          res = res.concat(chunk)
311
+        })
312
+        ifstream.onEnd(() => {
313
+          res = res.map((byte) => {
314
+            return String.fromCharCode(byte)
315
+          }).join('')
316
+          report(
317
+            <Assert key="data written in ASCII format should correct"
318
+              expect={expect + 'gg'}
319
+              actual={res}
320
+            />)
321
+              done()
322
+            })
331 323
       })
332 324
     })
333 325
 })
@@ -344,16 +336,42 @@ describe('ASCII file test', (report, done) => {
344 336
     return fs.createFile(p + filename, getASCIIArray(expect), 'ascii')
345 337
   })
346 338
   .then(() => {
347
-    let rs = fs.readStream(p + filename, 'base64')
348
-    let actual = ''
349
-    rs.onData((chunk) => {
350
-      actual += chunk
339
+    fs.readStream(p + filename, 'base64').then((rs) => {
340
+      let actual = ''
341
+      rs.open()
342
+      rs.onData((chunk) => {
343
+        actual += chunk
344
+      })
345
+      rs.onEnd(() => {
346
+        report(<Assert key="written data verify"
347
+          expect={expect}
348
+          actual={base64.decode(actual)}/>)
349
+        done()
350
+      })
351 351
     })
352
-    rs.onEnd(() => {
353
-      report(<Assert key="written data verify"
354
-        expect={expect}
355
-        actual={base64.decode(actual)}/>)
356
-      done()
352
+  })
353
+})
354
+
355
+describe('format conversion', (report, done) => {
356
+  let p = ''
357
+  fs.getSystemDirs().then((dirs) => {
358
+    p = dirs.DocumentDir + '/foo'
359
+    return fs.createFile(p, [102, 111, 111], 'ascii')
360
+  })
361
+  .then(() => {
362
+    fs.readStream(p, 'utf8').then((stream) => {
363
+      let res = []
364
+      stream.open()
365
+      stream.onData((chunk) => {
366
+        res+=chunk
367
+      })
368
+      stream.onEnd(() => {
369
+        report(
370
+          <Assert key="write utf8 and read by ascii"
371
+            expect="foo"
372
+            actual={res}/>)
373
+            done()
374
+      })
357 375
     })
358 376
   })
359 377
 })