Browse Source

Add larger examples, actually test streams

We look into the file contents to see if our utility outputs correctly.
master
TheoryOfNekomata 3 years ago
parent
commit
b772227f38
7 changed files with 97 additions and 29 deletions
  1. +4
    -0
      package.json
  2. +8
    -1
      src/Converter.ts
  3. +4
    -5
      src/ConverterStream.ts
  4. +1
    -1
      src/common/index.ts
  5. +69
    -22
      tests/Converter.test.ts
  6. +1
    -0
      tests/input/example7.txt
  7. +10
    -0
      yarn.lock

+ 4
- 0
package.json View File

@@ -27,11 +27,15 @@
}
},
"devDependencies": {
"@types/big.js": "^4.0.5",
"@types/node": "^14.6.0",
"fast-check": "^2.1.0",
"husky": "^4.2.5",
"tsdx": "^0.13.2",
"tslib": "^2.0.1",
"typescript": "^3.9.7"
},
"dependencies": {
"big.js": "^5.2.2"
}
}

+ 8
- 1
src/Converter.ts View File

@@ -1,3 +1,4 @@
import Big from 'big.js'
import ConverterStream from './ConverterStream'
import { ReadStreamOptions, ConvertOptions, NumberSystem, Digit } from './common'

@@ -6,10 +7,16 @@ export default class Converter {

public convert = (value: string | number | bigint): string => {
let converted: string[] = []
const valueStr = value as string
switch (typeof value!) {
case 'string':
// TODO bail out when exponent is too large!
try {
return this.convert(BigInt(new Big(valueStr).toFixed()))
} catch (e) {}
return this.convert(BigInt(valueStr))
case 'number':
return this.convert(BigInt(value))
return this.convert(BigInt(value as number))
case 'bigint':
let current = value as bigint
let thousandPower = 0n


+ 4
- 5
src/ConverterStream.ts View File

@@ -2,25 +2,24 @@ import { Transform } from 'stream'
import { StreamOptions } from './common'

export default class ConverterStream extends Transform {
private bigintBuffer: bigint = 0n
private buffer: string

constructor(
private readonly converter: (value: string | number | bigint) => string,
readonly options: StreamOptions,
) {
super()
this.buffer = ''
}

_transform(chunk: Buffer, _encoding: BufferEncoding, callback: Function) {
let chunkStr = chunk.toString(this.options.encoding)
// we might run out of allocation for bigints?
this.bigintBuffer *= 10n ** BigInt(chunkStr.length)
this.bigintBuffer += BigInt(chunkStr)
this.buffer += chunkStr
callback()
}

_flush(callback: Function) {
this.push(this.converter(this.bigintBuffer), this.options.encoding)
this.push(this.converter(this.buffer.trim()), this.options.encoding)
callback()
}
}

+ 1
- 1
src/common/index.ts View File

@@ -1,4 +1,4 @@
export type Scale = 'long' | 'short'
export type Scale = 'europeanLong' | 'long' | 'short'

export interface ConvertOptions {
scale?: Scale


+ 69
- 22
tests/Converter.test.ts View File

@@ -1,12 +1,15 @@
import fs from 'fs'
import Converter from '../src'
import English from '../src/systems/en'
import { Scale } from '../src/common'

describe.each`
name | system | scale
${'English'} | ${English} | ${'short'}
${'English'} | ${English} | ${'long'}
`('$name ($scale count)', ({ system, scale, }) => {
locale | name | system | scaleRaw
${'en'} | ${'English'} | ${English} | ${'short'}
${'en'} | ${'English'} | ${English} | ${'long'}
${'en'} | ${'English'} | ${English} | ${'europeanLong'}
`('$name ($scale count)', ({ locale, system, scaleRaw, }) => {
const scale = scaleRaw as Scale
let converter: Converter

beforeAll(() => {
@@ -18,6 +21,7 @@ describe.each`
value | name
${1000000} | ${'one million'}
${123456789} | ${'one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine'}
${'1e+6'} | ${'one million'}
`('should parse $value as $name', ({ value, name, }) => {
expect(converter.convert(value)).toBe(name)
})
@@ -26,31 +30,74 @@ describe.each`
describe('on streams', () => {
const ENCODING = 'utf-8'

test.each([
'million',
'example1',
'example2',
'example3',
'example4',
'example5',
'example6',
])('should correctly parse a stream', async (filename) => {
const inputStream = fs.createReadStream(
`./tests/input/${filename}.txt`,
{
encoding: ENCODING,
}
)
const expected: Record<string, Record<Scale, string>> = {
'million': {
short: 'one million',
long: 'one million',
europeanLong: 'one million',
},
'example1': {
short: 'nine hundred eighty seven million six hundred fifty four thousand three hundred twenty one',
long: 'nine hundred eighty seven million six hundred fifty four thousand three hundred twenty one',
europeanLong: 'nine hundred eighty seven million six hundred fifty four thousand three hundred twenty one',
},
'example2': {
short: 'one hundred twenty three quadrillion four hundred fifty six trillion seven hundred eighty nine billion two hundred forty six million eight hundred one thousand three hundred fifty seven',
long: 'one hundred twenty three thousand billion four hundred fifty six billion seven hundred eighty nine thousand million two hundred forty six million eight hundred one thousand three hundred fifty seven',
europeanLong: 'one hundred twenty three billiard four hundred fifty six billion seven hundred eighty nine milliard two hundred forty six million eight hundred one thousand three hundred fifty seven',
},
'example3': {
short: 'one hundred twenty three duoseptuagintillion four hundred fifty six unseptuagintillion seven hundred eighty nine septuagintillion two hundred forty six novemsexagintillion eight hundred one octosexagintillion three hundred fifty seven septensexagintillion',
long: 'one hundred twenty three thousand sextrigintillion four hundred fifty six sextrigintillion seven hundred eighty nine thousand quintrigintillion two hundred forty six quintrigintillion eight hundred one thousand quattuortrigintillion three hundred fifty seven quattuortrigintillion',
europeanLong: 'one hundred twenty three sextrigintilliard four hundred fifty six sextrigintillion seven hundred eighty nine quintrigintilliard two hundred forty six quintrigintillion eight hundred one quattuortrigintilliard three hundred fifty seven quattuortrigintillion',
},
'example4': {
short: 'one hundred twenty three cenduoseptuagintillion four hundred fifty six cenunseptuagintillion seven hundred eighty nine censeptuagintillion two hundred forty six cennovemsexagintillion eight hundred one cenoctosexagintillion three hundred fifty seven censeptensexagintillion',
long: 'one hundred twenty three thousand sexoctogintillion four hundred fifty six sexoctogintillion seven hundred eighty nine thousand quinoctogintillion two hundred forty six quinoctogintillion eight hundred one thousand quattuoroctogintillion three hundred fifty seven quattuoroctogintillion',
europeanLong: 'one hundred twenty three sexoctogintilliard four hundred fifty six sexoctogintillion seven hundred eighty nine quinoctogintilliard two hundred forty six quinoctogintillion eight hundred one quattuoroctogintilliard three hundred fifty seven quattuoroctogintillion',
},
'example5': {
short: 'one hundred twenty three trecenduoseptuagintillion four hundred fifty six trecenunseptuagintillion seven hundred eighty nine trecenseptuagintillion two hundred forty six trecennovemsexagintillion eight hundred one trecenoctosexagintillion three hundred fifty seven trecenseptensexagintillion',
long: 'one hundred twenty three thousand censexoctogintillion four hundred fifty six censexoctogintillion seven hundred eighty nine thousand cenquinoctogintillion two hundred forty six cenquinoctogintillion eight hundred one thousand cenquattuoroctogintillion three hundred fifty seven cenquattuoroctogintillion',
europeanLong: 'one hundred twenty three censexoctogintilliard four hundred fifty six censexoctogintillion seven hundred eighty nine cenquinoctogintilliard two hundred forty six cenquinoctogintillion eight hundred one cenquattuoroctogintilliard three hundred fifty seven cenquattuoroctogintillion',
},
'example6': {
short: 'one hundred twenty three milliaduoseptuagintillion four hundred fifty six milliaunseptuagintillion seven hundred eighty nine milliaseptuagintillion two hundred forty six millianovemsexagintillion eight hundred one milliaoctosexagintillion three hundred fifty seven milliaseptensexagintillion',
long: 'one hundred twenty three thousand quingensextrigintillion four hundred fifty six quingensextrigintillion seven hundred eighty nine thousand quingenquintrigintillion two hundred forty six quingenquintrigintillion eight hundred one thousand quingenquattuortrigintillion three hundred fifty seven quingenquattuortrigintillion',
europeanLong: 'one hundred twenty three quingensextrigintilliard four hundred fifty six quingensextrigintillion seven hundred eighty nine quingenquintrigintilliard two hundred forty six quingenquintrigintillion eight hundred one quingenquattuortrigintilliard three hundred fifty seven quingenquattuortrigintillion',
},
'example7': {
short: 'one hundred twenty three duomilliaduoseptuagintillion four hundred fifty six duomilliaunseptuagintillion seven hundred eighty nine duomilliaseptuagintillion two hundred forty six duomillianovemsexagintillion eight hundred one duomilliaoctosexagintillion three hundred fifty seven duomilliaseptensexagintillion',
long: 'one hundred twenty three thousand milliasextrigintillion four hundred fifty six milliasextrigintillion seven hundred eighty nine thousand milliaquintrigintillion two hundred forty six milliaquintrigintillion eight hundred one thousand milliaquattuortrigintillion three hundred fifty seven milliaquattuortrigintillion',
europeanLong: 'one hundred twenty three milliasextrigintilliard four hundred fifty six milliasextrigintillion seven hundred eighty nine milliaquintrigintilliard two hundred forty six milliaquintrigintillion eight hundred one milliaquattuortrigintilliard three hundred fifty seven milliaquattuortrigintillion',
},
}

beforeAll(() => {
try { fs.mkdirSync('./tests/output') } catch {}
try { fs.mkdirSync(`./tests/output/${locale}`) } catch {}
})

test.each(Object.keys(expected))('should correctly parse %s.txt', async (filename) => {
const inputPath = `./tests/input/${filename}.txt`
const outputPath = `./tests/output/${locale}/${filename}.${scale}.txt`

const inputStream = fs.createReadStream(inputPath, { encoding: ENCODING, })
const outputStream = fs.createWriteStream(outputPath, { encoding: ENCODING, })

const readStream = converter.readStream({
encoding: ENCODING
})

inputStream
.pipe(readStream)
.pipe(fs.createWriteStream(`./tests/output/${filename}.${scale}.txt`))
.pipe(outputStream)

return new Promise((resolve) => {
readStream.on('end', () => {
resolve('Hello')
outputStream.on('close', () => {
const output = fs.readFileSync(outputPath).toString(ENCODING)
expect(output).toBe(expected[filename][scale])
resolve()
})
})
})


+ 1
- 0
tests/input/example7.txt View File

@@ -0,0 +1 @@
1.23456789246801357e+6221

+ 10
- 0
yarn.lock View File

@@ -1093,6 +1093,11 @@
dependencies:
"@babel/types" "^7.3.0"

"@types/big.js@^4.0.5":
version "4.0.5"
resolved "https://registry.yarnpkg.com/@types/big.js/-/big.js-4.0.5.tgz#62c61697646269e39191f24e55e8272f05f21fc0"
integrity sha512-D9KFrAt05FDSqLo7PU9TDHfDgkarlwdkuwFsg7Zm4xl62tTNaz+zN+Tkcdx2wGLBbSMf8BnoMhOVeUGUaJfLKg==

"@types/color-name@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
@@ -1643,6 +1648,11 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"

big.js@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==

bindings@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"