- Introduced `test-fallback-function.js` and `test-fallback-simple.js` to validate `executeInThread` fallback functionality with both named and anonymous compute-intensive functions. - Added `test-tensorflow-textencoder.js` for TensorFlow.js and TextEncoder tests in a Node.js environment. - Created `test-tensorflow-textencoder.html` for browser-based TensorFlow.js and TextEncoder tests. - Implemented cross-environment test support in `cli-package/src/test-tensorflow-textencoder.ts` for CLI functionality. - Enhanced `src/utils/embedding.ts`, `textEncoding.ts`, and `brainy-wrapper.js` to include updated global `TextEncoder` and `TextDecoder` utilities for compatibility and worker improvements. - Standardized and expanded utility methods in `PlatformNode` for broader support, including `isFloat32Array` and `isTypedArray` checks. - Updated Node.js requirement to `>= 24.4.0` across documentation and configuration files for compatibility improvements. This update introduces comprehensive testing for fallback mechanisms, TensorFlow.js, and TextEncoder across multiple environments, ensuring robustness and compatibility.
300 lines
9.8 KiB
JavaScript
300 lines
9.8 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Test All Environments
|
|
*
|
|
* This script runs tests for the Brainy library in all environments:
|
|
* - Browser (using Puppeteer for headless browser testing)
|
|
* - Node.js
|
|
* - CLI
|
|
*/
|
|
|
|
import { execSync } from 'child_process'
|
|
import { fileURLToPath } from 'url'
|
|
import path from 'path'
|
|
import fs from 'fs'
|
|
import http from 'http'
|
|
import puppeteer from 'puppeteer'
|
|
|
|
// Get the directory of the current module
|
|
const __filename = fileURLToPath(import.meta.url)
|
|
const __dirname = path.dirname(__filename)
|
|
const rootDir = path.join(__dirname, '..')
|
|
|
|
// Define colors for console output
|
|
const colors = {
|
|
reset: '\x1b[0m',
|
|
bright: '\x1b[1m',
|
|
green: '\x1b[32m',
|
|
yellow: '\x1b[33m',
|
|
red: '\x1b[31m',
|
|
cyan: '\x1b[36m'
|
|
}
|
|
|
|
// Helper function to log with colors
|
|
function log(message, color = colors.reset) {
|
|
console.log(`${color}${message}${colors.reset}`)
|
|
}
|
|
|
|
// Helper function to log section headers
|
|
function logSection(title) {
|
|
console.log('\n' + '='.repeat(80))
|
|
console.log(`${colors.bright}${colors.cyan}${title}${colors.reset}`)
|
|
console.log('='.repeat(80) + '\n')
|
|
}
|
|
|
|
// Helper function to run a command and return its output
|
|
function runCommand(command, cwd = rootDir) {
|
|
try {
|
|
return execSync(command, { stdio: 'pipe', cwd, encoding: 'utf8' })
|
|
} catch (error) {
|
|
log(`Error running command: ${command}`, colors.red)
|
|
log(error.message, colors.red)
|
|
if (error.stdout) log(`stdout: ${error.stdout}`)
|
|
if (error.stderr) log(`stderr: ${error.stderr}`, colors.red)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
// Main function to run all tests
|
|
async function runAllTests() {
|
|
try {
|
|
logSection('BUILDING PACKAGES')
|
|
|
|
// Build the main package
|
|
log('Building main package...', colors.yellow)
|
|
runCommand('npm run build')
|
|
log('Main package built successfully!', colors.green)
|
|
|
|
// Apply TextEncoder patch
|
|
log('Applying TextEncoder patch...', colors.yellow)
|
|
runCommand('node scripts/patch-textencoder.js')
|
|
log('TextEncoder patch applied successfully!', colors.green)
|
|
|
|
// Build the browser package
|
|
log('Building browser package...', colors.yellow)
|
|
runCommand('npm run build:browser')
|
|
log('Browser package built successfully!', colors.green)
|
|
|
|
// Build the CLI package
|
|
log('Building CLI package...', colors.yellow)
|
|
runCommand('npm run build:cli')
|
|
log('CLI package built successfully!', colors.green)
|
|
|
|
logSection('RUNNING NODE.JS TESTS')
|
|
|
|
// Run Node.js tests
|
|
log('Running Node.js worker test...', colors.yellow)
|
|
const nodeWorkerResult = runCommand('node test-worker.js')
|
|
log(nodeWorkerResult)
|
|
log('Node.js worker test completed!', colors.green)
|
|
|
|
log('Running unified text encoding test...', colors.yellow)
|
|
const textEncodingResult = runCommand('node test-unified-encoding.js')
|
|
log(textEncodingResult)
|
|
log('Unified text encoding test completed!', colors.green)
|
|
|
|
log('Running TensorFlow and TextEncoder test...', colors.yellow)
|
|
const tensorflowTextEncoderResult = runCommand('node test-tensorflow-textencoder.js')
|
|
log(tensorflowTextEncoderResult)
|
|
log('TensorFlow and TextEncoder test completed!', colors.green)
|
|
|
|
logSection('RUNNING BROWSER TESTS')
|
|
|
|
// Start a simple HTTP server to serve the test files
|
|
log('Starting HTTP server...', colors.yellow)
|
|
const server = http.createServer((req, res) => {
|
|
// Normalize the URL to handle relative paths
|
|
const normalizedUrl = req.url.replace(/^\/+/, '/')
|
|
let filePath = path.join(
|
|
rootDir,
|
|
normalizedUrl === '/' ? 'index.html' : normalizedUrl
|
|
)
|
|
|
|
// Handle relative paths (e.g., ../dist/unified.js)
|
|
if (normalizedUrl.includes('../')) {
|
|
// Convert the URL to an absolute path relative to the root directory
|
|
const parts = normalizedUrl.split('/')
|
|
const resolvedParts = []
|
|
|
|
for (const part of parts) {
|
|
if (part === '..') {
|
|
resolvedParts.pop()
|
|
} else if (part && part !== '.') {
|
|
resolvedParts.push(part)
|
|
}
|
|
}
|
|
|
|
filePath = path.join(rootDir, resolvedParts.join('/'))
|
|
}
|
|
|
|
log(`Request for: ${req.url}, resolved to: ${filePath}`, colors.yellow)
|
|
|
|
// Check if the file exists
|
|
if (fs.existsSync(filePath)) {
|
|
const extname = path.extname(filePath)
|
|
let contentType = 'text/html'
|
|
|
|
switch (extname) {
|
|
case '.js':
|
|
contentType = 'text/javascript'
|
|
break
|
|
case '.css':
|
|
contentType = 'text/css'
|
|
break
|
|
case '.json':
|
|
contentType = 'application/json'
|
|
break
|
|
case '.png':
|
|
contentType = 'image/png'
|
|
break
|
|
case '.jpg':
|
|
contentType = 'image/jpg'
|
|
break
|
|
}
|
|
|
|
res.writeHead(200, { 'Content-Type': contentType })
|
|
const fileStream = fs.createReadStream(filePath)
|
|
fileStream.pipe(res)
|
|
} else {
|
|
log(`File not found: ${filePath}`, colors.red)
|
|
res.writeHead(404)
|
|
res.end('File not found')
|
|
}
|
|
})
|
|
|
|
// Start the server on a random port
|
|
const PORT = 3000 + Math.floor(Math.random() * 1000)
|
|
server.listen(PORT)
|
|
log(`HTTP server started on port ${PORT}`, colors.green)
|
|
|
|
// Run browser tests using Puppeteer
|
|
log('Launching headless browser...', colors.yellow)
|
|
// Using --no-sandbox flag to avoid issues with the Chrome sandbox in certain environments
|
|
// See: https://chromium.googlesource.com/chromium/src/+/main/docs/linux/suid_sandbox_development.md
|
|
const browser = await puppeteer.launch({ args: ['--no-sandbox'] })
|
|
const page = await browser.newPage()
|
|
|
|
// Capture console logs from the page
|
|
page.on('console', (message) => {
|
|
const type = message.type()
|
|
const text = message.text()
|
|
if (type === 'error') {
|
|
log(`Browser console error: ${text}`, colors.red)
|
|
} else {
|
|
log(`Browser console: ${text}`)
|
|
}
|
|
})
|
|
|
|
// Test browser worker
|
|
log('Running browser worker test...', colors.yellow)
|
|
await page.goto(`http://localhost:${PORT}/demo/test-browser-worker.html`)
|
|
await page.waitForSelector('#runTest')
|
|
await page.click('#runTest')
|
|
await page.waitForFunction(
|
|
() => {
|
|
const resultText = document.getElementById('result').textContent
|
|
return resultText.includes('Worker thread execution completed')
|
|
},
|
|
{ timeout: 30000 }
|
|
)
|
|
|
|
const browserWorkerResult = await page.evaluate(() => {
|
|
return document.getElementById('result').innerHTML
|
|
})
|
|
log('Browser worker test result:', colors.green)
|
|
log(browserWorkerResult.replace(/<[^>]*>/g, '').trim())
|
|
|
|
// Test fallback mechanism
|
|
log('Running fallback test...', colors.yellow)
|
|
await page.goto(`http://localhost:${PORT}/demo/test-fallback.html`)
|
|
await page.waitForSelector('#runTest')
|
|
await page.click('#runTest')
|
|
await page.waitForFunction(
|
|
() => {
|
|
const resultText = document.getElementById('result').textContent
|
|
return resultText.includes('Test completed')
|
|
},
|
|
{ timeout: 30000 }
|
|
)
|
|
|
|
const fallbackResult = await page.evaluate(() => {
|
|
return document.getElementById('result').innerHTML
|
|
})
|
|
log('Fallback test result:', colors.green)
|
|
log(fallbackResult.replace(/<[^>]*>/g, '').trim())
|
|
|
|
// Test TensorFlow and TextEncoder in browser
|
|
log('Running TensorFlow and TextEncoder browser test...', colors.yellow)
|
|
await page.goto(`http://localhost:${PORT}/demo/test-tensorflow-textencoder.html`)
|
|
await page.waitForSelector('#runTest')
|
|
await page.click('#runTest')
|
|
await page.waitForFunction(
|
|
() => {
|
|
const resultText = document.getElementById('result').textContent
|
|
return resultText.includes('Test completed')
|
|
},
|
|
{ timeout: 30000 }
|
|
)
|
|
|
|
const browserTensorflowTextEncoderResult = await page.evaluate(() => {
|
|
return document.getElementById('result').innerHTML
|
|
})
|
|
log('TensorFlow and TextEncoder browser test result:', colors.green)
|
|
log(browserTensorflowTextEncoderResult.replace(/<[^>]*>/g, '').trim())
|
|
|
|
// Close the browser and server
|
|
await browser.close()
|
|
server.close()
|
|
log('HTTP server stopped', colors.green)
|
|
|
|
logSection('RUNNING CLI TESTS')
|
|
|
|
// Run CLI tests
|
|
log('Testing CLI package locally...', colors.yellow)
|
|
try {
|
|
runCommand('npm run test:cli')
|
|
log('CLI test completed!', colors.green)
|
|
|
|
// Run some basic CLI commands to verify functionality
|
|
log('Testing basic CLI commands...', colors.yellow)
|
|
const cliVersionResult = runCommand('brainy --version')
|
|
log(`CLI version: ${cliVersionResult.trim()}`, colors.green)
|
|
|
|
const cliHelpResult = runCommand('brainy --help')
|
|
log('CLI help command executed successfully', colors.green)
|
|
|
|
// Test the pipeline command
|
|
log('Testing pipeline command...', colors.yellow)
|
|
const pipelineResult = runCommand('brainy test-pipeline "This is a test"')
|
|
log('Pipeline test completed!', colors.green)
|
|
|
|
// Test TensorFlow and TextEncoder in CLI
|
|
log('Testing TensorFlow and TextEncoder in CLI...', colors.yellow)
|
|
const cliTensorflowResult = runCommand('brainy test-tensorflow-textencoder')
|
|
log('TensorFlow and TextEncoder CLI test completed!', colors.green)
|
|
} catch (error) {
|
|
log(
|
|
"CLI tests failed. This might be expected if you don't have the CLI installed globally.",
|
|
colors.yellow
|
|
)
|
|
log(
|
|
'You can run the CLI tests separately with: npm run test:cli',
|
|
colors.yellow
|
|
)
|
|
}
|
|
|
|
logSection('ALL TESTS COMPLETED')
|
|
log('All environment tests completed successfully!', colors.green)
|
|
} catch (error) {
|
|
logSection('TEST FAILURE')
|
|
log(`Tests failed: ${error.message}`, colors.red)
|
|
process.exit(1)
|
|
}
|
|
}
|
|
|
|
// Run the tests
|
|
runAllTests().catch((error) => {
|
|
log(`Unhandled error: ${error.message}`, colors.red)
|
|
process.exit(1)
|
|
})
|