Decompress images before flashing, remove trim setting, trim ext partitions

Changelog-entry: Decompress images before flashing, remove trim setting, trim ext partitions
Change-type: patch
This commit is contained in:
Alexis Svinartchouk 2020-04-23 17:06:21 +02:00
parent e6125b893d
commit ee62b9a4c7
16 changed files with 202 additions and 386 deletions

View File

@ -89,30 +89,43 @@ analytics.logEvent('Application start', {
applicationSessionUuid,
});
const debouncedLog = _.debounce(console.log, 1000, { maxWait: 1000 });
function pluralize(word: string, quantity: number) {
return `${quantity} ${word}${quantity === 1 ? '' : 's'}`;
}
observe(() => {
if (!flashState.isFlashing()) {
return;
}
const currentFlashState = flashState.getFlashState();
const stateType =
!currentFlashState.flashing && currentFlashState.verifying
? `Verifying ${currentFlashState.verifying}`
: `Flashing ${currentFlashState.flashing}`;
windowProgress.set(currentFlashState);
let eta = '';
if (currentFlashState.eta !== undefined) {
eta = `eta in ${currentFlashState.eta.toFixed(0)}s`;
}
let active = '';
if (currentFlashState.type !== 'decompressing') {
active = pluralize('device', currentFlashState.active);
}
// NOTE: There is usually a short time period between the `isFlashing()`
// property being set, and the flashing actually starting, which
// might cause some non-sense flashing state logs including
// `undefined` values.
analytics.logDebug(
`${stateType} devices, ` +
`${currentFlashState.percentage}% at ${currentFlashState.speed} MB/s ` +
`(total ${currentFlashState.totalSpeed} MB/s) ` +
`eta in ${currentFlashState.eta}s ` +
`with ${currentFlashState.failed} failed devices`,
);
windowProgress.set(currentFlashState);
debouncedLog(outdent({ newline: ' ' })`
${_.capitalize(currentFlashState.type)}
${active},
${currentFlashState.percentage}%
at
${(currentFlashState.speed || 0).toFixed(2)}
MB/s
(total ${(currentFlashState.speed * currentFlashState.active).toFixed(2)} MB/s)
${eta}
with
${pluralize('failed device', currentFlashState.failed)}
`);
});
/**

View File

@ -14,39 +14,11 @@
* limitations under the License.
*/
import * as Color from 'color';
import * as React from 'react';
import { ProgressBar } from 'rendition';
import { css, default as styled, keyframes } from 'styled-components';
import { default as styled } from 'styled-components';
import { StepButton, StepSelection } from '../../styled-components';
import { colors } from '../../theme';
const darkenForegroundStripes = 0.18;
const desaturateForegroundStripes = 0.2;
const progressButtonStripesForegroundColor = Color(colors.primary.background)
.darken(darkenForegroundStripes)
.desaturate(desaturateForegroundStripes)
.string();
const desaturateBackgroundStripes = 0.05;
const progressButtonStripesBackgroundColor = Color(colors.primary.background)
.desaturate(desaturateBackgroundStripes)
.string();
const ProgressButtonStripes = keyframes`
0% {
background-position: 0 0;
}
100% {
background-position: 20px 20px;
}
`;
const ProgressButtonStripesRule = css`
${ProgressButtonStripes} 1s linear infinite;
`;
import { StepButton } from '../../styled-components';
const FlashProgressBar = styled(ProgressBar)`
> div {
@ -54,6 +26,10 @@ const FlashProgressBar = styled(ProgressBar)`
height: 48px;
color: white !important;
text-shadow: none !important;
transition-duration: 0s;
> div {
transition-duration: 0s;
}
}
width: 200px;
@ -61,40 +37,11 @@ const FlashProgressBar = styled(ProgressBar)`
font-size: 16px;
line-height: 48px;
background: ${Color(colors.warning.background)
.darken(darkenForegroundStripes)
.string()};
`;
const FlashProgressBarValidating = styled(FlashProgressBar)`
// Notice that we add 0.01 to certain gradient stop positions.
// That workarounds a Chrome rendering issue where diagonal
// lines look spiky.
// See https://github.com/balena-io/etcher/issues/472
background-image: -webkit-gradient(
linear,
0 0,
100% 100%,
color-stop(0.25, ${progressButtonStripesForegroundColor}),
color-stop(0.26, ${progressButtonStripesBackgroundColor}),
color-stop(0.5, ${progressButtonStripesBackgroundColor}),
color-stop(0.51, ${progressButtonStripesForegroundColor}),
color-stop(0.75, ${progressButtonStripesForegroundColor}),
color-stop(0.76, ${progressButtonStripesBackgroundColor}),
to(${progressButtonStripesBackgroundColor})
);
background-color: white;
animation: ${ProgressButtonStripesRule};
overflow: hidden;
background-size: 20px 20px;
background: #2f3033;
`;
interface ProgressButtonProps {
striped: boolean;
type: 'decompressing' | 'flashing' | 'verifying';
active: boolean;
percentage: number;
label: string;
@ -102,45 +49,35 @@ interface ProgressButtonProps {
callback: () => any;
}
const colors = {
decompressing: '#00aeef',
flashing: '#da60ff',
verifying: '#1ac135',
} as const;
/**
* Progress Button component
*/
export class ProgressButton extends React.Component<ProgressButtonProps> {
public render() {
if (this.props.active) {
if (this.props.striped) {
return (
<StepSelection>
<FlashProgressBarValidating
primary
emphasized
value={this.props.percentage}
>
{this.props.label}
</FlashProgressBarValidating>
</StepSelection>
);
}
return (
<StepSelection>
<FlashProgressBar warning emphasized value={this.props.percentage}>
{this.props.label}
</FlashProgressBar>
</StepSelection>
);
}
return (
<StepSelection>
<StepButton
primary
onClick={this.props.callback}
disabled={this.props.disabled}
<FlashProgressBar
background={colors[this.props.type]}
value={this.props.percentage}
>
{this.props.label}
</StepButton>
</StepSelection>
</FlashProgressBar>
);
}
return (
<StepButton
primary
onClick={this.props.callback}
disabled={this.props.disabled}
>
{this.props.label}
</StepButton>
);
}
}

View File

@ -87,10 +87,6 @@ const settingsList: Setting[] = [
name: 'validateWriteOnSuccess',
label: 'Validate write on success',
},
{
name: 'trim',
label: 'Trim ext{2,3,4} partitions before writing (raw images only)',
},
{
name: 'updatesEnabled',
label: 'Auto-updates enabled',

View File

@ -375,7 +375,7 @@ export class SourceSelector extends React.Component<
analytics.logEvent('Unsupported protocol', { path: imagePath });
return;
}
source = new sourceDestination.Http(imagePath);
source = new sourceDestination.Http({ url: imagePath });
}
try {

View File

@ -85,13 +85,6 @@ export function setProgressState(
return _.round(bytesToMegabytes(state.speed), PRECISION);
}
return null;
}),
totalSpeed: _.attempt(() => {
if (_.isFinite(state.totalSpeed)) {
return _.round(bytesToMegabytes(state.totalSpeed), PRECISION);
}
return null;
}),
});

View File

@ -29,13 +29,14 @@ export const DEFAULT_SETTINGS: _.Dictionary<any> = {
errorReporting: true,
unmountOnSuccess: true,
validateWriteOnSuccess: true,
trim: false,
updatesEnabled:
packageJSON.updates.enabled &&
!_.includes(['rpm', 'deb'], packageJSON.packageType),
lastSleptUpdateNotifier: null,
lastSleptUpdateNotifierVersion: null,
desktopNotifications: true,
autoBlockmapping: true,
decompressFirst: true,
};
let settings = _.cloneDeep(DEFAULT_SETTINGS);

View File

@ -45,7 +45,7 @@ function verifyNoNilFields(
/**
* @summary FLASH_STATE fields that can't be nil
*/
const flashStateNoNilFields = ['speed', 'totalSpeed'];
const flashStateNoNilFields = ['speed'];
/**
* @summary SELECT_IMAGE fields that can't be nil
@ -65,14 +65,11 @@ const DEFAULT_STATE = Immutable.fromJS({
isFlashing: false,
flashResults: {},
flashState: {
flashing: 0,
verifying: 0,
successful: 0,
active: 0,
failed: 0,
percentage: 0,
speed: null,
averageSpeed: null,
totalSpeed: null,
},
lastAverageFlashingSpeed: null,
});
@ -234,17 +231,7 @@ function storeReducer(
verifyNoNilFields(action.data, flashStateNoNilFields, 'flash');
if (
!_.every(
_.pick(action.data, [
'flashing',
'verifying',
'successful',
'failed',
]),
_.isFinite,
)
) {
if (!_.every(_.pick(action.data, ['active', 'failed']), _.isFinite)) {
throw errors.createError({
title: 'State quantity field(s) not finite number',
});
@ -266,7 +253,7 @@ function storeReducer(
}
let ret = state.set('flashState', Immutable.fromJS(action.data));
if (action.data.flashing) {
if (action.data.type === 'flashing') {
ret = ret.set('lastAverageFlashingSpeed', action.data.averageSpeed);
}
return ret;

View File

@ -168,7 +168,6 @@ export function performWrite(
flashInstanceUuid: flashState.getFlashUuid(),
unmountOnSuccess: settings.get('unmountOnSuccess'),
validateWriteOnSuccess: settings.get('validateWriteOnSuccess'),
trim: settings.get('trim'),
};
ipc.server.on('fail', ({ error }: { error: Error & { code: string } }) => {
@ -196,8 +195,9 @@ export function performWrite(
source,
SourceType: source.SourceType.name,
validateWriteOnSuccess: settings.get('validateWriteOnSuccess'),
trim: settings.get('trim'),
autoBlockmapping: settings.get('autoBlockmapping'),
unmountOnSuccess: settings.get('unmountOnSuccess'),
decompressFirst: settings.get('decompressFirst'),
});
});
@ -273,7 +273,6 @@ export async function flash(
flashInstanceUuid: flashState.getFlashUuid(),
unmountOnSuccess: settings.get('unmountOnSuccess'),
validateWriteOnSuccess: settings.get('validateWriteOnSuccess'),
trim: settings.get('trim'),
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
};
@ -318,6 +317,8 @@ export async function flash(
errors: results.errors,
devices: results.devices,
status: 'finished',
bytesWritten: results.bytesWritten,
sourceMetadata: results.sourceMetadata,
};
analytics.logEvent('Done', eventData);
}
@ -336,7 +337,6 @@ export function cancel() {
flashInstanceUuid: flashState.getFlashUuid(),
unmountOnSuccess: settings.get('unmountOnSuccess'),
validateWriteOnSuccess: settings.get('validateWriteOnSuccess'),
trim: settings.get('trim'),
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid,
status: 'cancel',

View File

@ -15,16 +15,15 @@
*/
import { bytesToClosestUnit } from '../../../shared/units';
import * as settings from '../models/settings';
// import * as settings from '../models/settings';
export interface FlashState {
flashing: number;
verifying: number;
successful: number;
active: number;
failed: number;
percentage?: number;
speed: number;
position: number;
type?: 'decompressing' | 'flashing' | 'verifying';
}
/**
@ -36,45 +35,47 @@ export interface FlashState {
*
* @example
* const status = progressStatus.fromFlashState({
* flashing: 1,
* verifying: 0,
* successful: 0,
* type: 'flashing'
* active: 1,
* failed: 0,
* percentage: 55,
* speed: 2049
* speed: 2049,
* })
*
* console.log(status)
* // '55% Flashing'
*/
export function fromFlashState(state: FlashState): string {
const isFlashing = Boolean(state.flashing);
const isValidating = !isFlashing && Boolean(state.verifying);
const shouldValidate = settings.get('validateWriteOnSuccess');
const shouldUnmount = settings.get('unmountOnSuccess');
if (state.percentage === 0 && !state.speed) {
if (isValidating) {
return 'Validating...';
}
export function fromFlashState({
type,
percentage,
position,
}: FlashState): string {
if (type === undefined) {
return 'Starting...';
} else if (state.percentage === 100) {
if ((isValidating || !shouldValidate) && shouldUnmount) {
return 'Unmounting...';
} else if (type === 'decompressing') {
if (percentage == null) {
return 'Decompressing...';
} else {
return `${percentage}% Decompressing`;
}
return 'Finishing...';
} else if (isFlashing) {
if (state.percentage != null) {
return `${state.percentage}% Flashing`;
} else if (type === 'flashing') {
if (percentage != null) {
if (percentage < 100) {
return `${percentage}% Flashing`;
} else {
return 'Finishing...';
}
} else {
return `${bytesToClosestUnit(position)} flashed`;
}
} else if (type === 'verifying') {
if (percentage == null) {
return 'Validating...';
} else if (percentage < 100) {
return `${percentage}% Validating`;
} else {
return 'Finishing...';
}
return `${bytesToClosestUnit(state.position)} flashed`;
} else if (isValidating) {
return `${state.percentage}% Validating`;
} else if (!isFlashing && !isValidating) {
return 'Failed';
}
throw new Error(`Invalid state: ${JSON.stringify(state)}`);
return 'Failed';
}

View File

@ -33,6 +33,7 @@ import { scanner as driveScanner } from '../../modules/drive-scanner';
import * as imageWriter from '../../modules/image-writer';
import * as progressStatus from '../../modules/progress-status';
import * as notification from '../../os/notification';
import { StepSelection } from '../../styled-components';
const COMPLETED_PERCENTAGE = 100;
const SPEED_PRECISION = 2;
@ -243,14 +244,16 @@ export const Flash = ({
</div>
<div className="space-vertical-large">
<ProgressButton
striped={state.type === 'verifying'}
active={isFlashing}
percentage={state.percentage}
label={getProgressButtonLabel()}
disabled={Boolean(flashErrorCode) || shouldFlashStepBeDisabled}
callback={tryFlash}
></ProgressButton>
<StepSelection>
<ProgressButton
type={state.type}
active={isFlashing}
percentage={state.percentage}
label={getProgressButtonLabel()}
disabled={Boolean(flashErrorCode) || shouldFlashStepBeDisabled}
callback={tryFlash}
/>
</StepSelection>
{isFlashing && (
<button

View File

@ -17,6 +17,7 @@
import { delay } from 'bluebird';
import { Drive as DrivelistDrive } from 'drivelist';
import * as sdk from 'etcher-sdk';
import { cleanupTmpFiles } from 'etcher-sdk/build/tmp';
import * as _ from 'lodash';
import * as ipc from 'node-ipc';
@ -77,6 +78,7 @@ interface WriteResult {
successful: number;
};
errors: Array<Error & { device: string }>;
sourceMetadata: sdk.sourceDestination.Metadata;
}
/**
@ -84,38 +86,42 @@ interface WriteResult {
* @param {SourceDestination} source - source
* @param {SourceDestination[]} destinations - destinations
* @param {Boolean} verify - whether to validate the writes or not
* @param {Boolean} trim - whether to trim ext partitions before writing
* @param {Boolean} autoBlockmapping - whether to trim ext partitions before writing
* @param {Function} onProgress - function to call on progress
* @param {Function} onFail - function to call on fail
* @returns {Promise<{ bytesWritten, devices, errors} >}
*/
async function writeAndValidate(
source: sdk.sourceDestination.SourceDestination,
destinations: sdk.sourceDestination.BlockDevice[],
verify: boolean,
trim: boolean,
onProgress: sdk.multiWrite.OnProgressFunction,
onFail: sdk.multiWrite.OnFailFunction,
): Promise<WriteResult> {
let innerSource: sdk.sourceDestination.SourceDestination = await source.getInnerSource();
if (trim && (await innerSource.canRead())) {
innerSource = new sdk.sourceDestination.ConfiguredSource({
source: innerSource,
shouldTrimPartitions: trim,
createStreamFromDisk: true,
});
}
async function writeAndValidate({
source,
destinations,
verify,
autoBlockmapping,
decompressFirst,
onProgress,
onFail,
}: {
source: sdk.sourceDestination.SourceDestination;
destinations: sdk.sourceDestination.BlockDevice[];
verify: boolean;
autoBlockmapping: boolean;
decompressFirst: boolean;
onProgress: sdk.multiWrite.OnProgressFunction;
onFail: sdk.multiWrite.OnFailFunction;
}): Promise<WriteResult> {
const {
sourceMetadata,
failures,
bytesWritten,
} = await sdk.multiWrite.pipeSourceToDestinations(
innerSource,
} = await sdk.multiWrite.decompressThenFlash({
source,
destinations,
onFail,
onProgress,
verify,
32,
);
trim: autoBlockmapping,
numBuffers: 32,
decompressFirst,
});
const result: WriteResult = {
bytesWritten,
devices: {
@ -123,6 +129,7 @@ async function writeAndValidate(
successful: destinations.length - failures.size,
},
errors: [],
sourceMetadata,
};
for (const [destination, error] of failures) {
const err = error as Error & { device: string };
@ -137,12 +144,15 @@ interface WriteOptions {
destinations: DrivelistDrive[];
unmountOnSuccess: boolean;
validateWriteOnSuccess: boolean;
trim: boolean;
autoBlockmapping: boolean;
decompressFirst: boolean;
source: SourceOptions;
SourceType: string;
}
ipc.connectTo(IPC_SERVER_ID, () => {
// Remove leftover tmp files older than 1 hour
cleanupTmpFiles(Date.now() - 60 * 60 * 1000);
process.once('uncaughtException', handleError);
// Gracefully exit on the following cases. If the parent
@ -219,7 +229,8 @@ ipc.connectTo(IPC_SERVER_ID, () => {
log(`Devices: ${destinations.join(', ')}`);
log(`Umount on success: ${options.unmountOnSuccess}`);
log(`Validate on success: ${options.validateWriteOnSuccess}`);
log(`Trim: ${options.trim}`);
log(`Auto blockmapping: ${options.autoBlockmapping}`);
log(`Decompress first: ${options.decompressFirst}`);
const dests = _.map(options.destinations, destination => {
return new sdk.sourceDestination.BlockDevice({
drive: destination,
@ -235,17 +246,18 @@ ipc.connectTo(IPC_SERVER_ID, () => {
path: options.imagePath,
});
} else {
source = new Http(options.imagePath);
source = new Http({ url: options.imagePath });
}
try {
const results = await writeAndValidate(
const results = await writeAndValidate({
source,
dests,
options.validateWriteOnSuccess,
options.trim,
destinations: dests,
verify: options.validateWriteOnSuccess,
autoBlockmapping: options.autoBlockmapping,
decompressFirst: options.decompressFirst,
onProgress,
onFail,
);
});
log(`Finish: ${results.bytesWritten}`);
results.errors = _.map(results.errors, error => {
return toJSON(error);

58
npm-shrinkwrap.json generated
View File

@ -1600,12 +1600,11 @@
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
},
"axios": {
"version": "0.18.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz",
"integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
"requires": {
"follow-redirects": "1.5.10",
"is-buffer": "^2.0.2"
"follow-redirects": "1.5.10"
}
},
"babel-code-frame": {
@ -1860,9 +1859,9 @@
},
"dependencies": {
"buffer": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.5.0.tgz",
"integrity": "sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww==",
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
"integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4"
@ -2469,6 +2468,11 @@
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
"integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
},
"check-disk-space": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/check-disk-space/-/check-disk-space-2.1.0.tgz",
"integrity": "sha512-f0nx9oJF/AVF8nhSYlF1EBvMNnO+CXyLwKhPvN1943iOMI9TWhQigLZm80jAf0wzQhwKkzA8XXjyvuVUeGGcVQ=="
},
"check-error": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
@ -2838,15 +2842,6 @@
"object-visit": "^1.0.0"
}
},
"color": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color/-/color-2.0.1.tgz",
"integrity": "sha512-ubUCVVKfT7r2w2D3qtHakj8mbmKms+tThR8gI8zEYCbUBl8/voqFGt3kgBqGwXAopgXybnkuOq+qMYCRrp4cXw==",
"requires": {
"color-convert": "^1.9.1",
"color-string": "^1.5.2"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -5412,14 +5407,15 @@
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
},
"etcher-sdk": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-3.0.1.tgz",
"integrity": "sha512-Jd30W0OfKNwbQZ4NdsNLCItyPYNP3hIugJrG/V5uzBtpL4R+gldMrUopkJ1L0x59d9NwWavQ/CqM6gxrv3tVlw==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-4.0.0.tgz",
"integrity": "sha512-yzvZEoZkGO+QnpSRF7VTsNAMxnXTgdWoPOUUg62DVRrCJSIFOBrRYHfCfSCih20qHNlQnFuWKjJgFiDdMjfKJA==",
"requires": {
"@ronomon/direct-io": "^3.0.1",
"axios": "^0.18.0",
"axios": "^0.19.2",
"blockmap": "^4.0.1",
"bluebird": "^3.5.1",
"check-disk-space": "^2.1.0",
"crc": "^3.8.0",
"debug": "^3.1.0",
"drivelist": "^8.0.4",
@ -8364,9 +8360,9 @@
}
},
"lzma-native": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lzma-native/-/lzma-native-6.0.0.tgz",
"integrity": "sha512-rf5f4opPymsPHotgY2d0cUP3kbVxERSxWDGEbi2gnbnxuWGokFrBaQ02Oe9pssIwsgp0r0PnbSNg7VPY3AYe7w==",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/lzma-native/-/lzma-native-6.0.1.tgz",
"integrity": "sha512-O6oWF0xe1AFvOCjU8uOZBZ/lhjaMNwHfVNaqVMqmoQXlRwBcFWpCAToiZOdXcKVMdo/5s/D0a2QgA5laMErxHQ==",
"requires": {
"node-addon-api": "^1.6.0",
"node-pre-gyp": "^0.11.0",
@ -9320,9 +9316,9 @@
}
},
"node-abi": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.15.0.tgz",
"integrity": "sha512-FeLpTS0F39U7hHZU1srAK4Vx+5AHNVOTP+hxBNQknR/54laTHSFIJkDWDqiquY1LeLUgTfPN7sLPhMubx0PLAg==",
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.16.0.tgz",
"integrity": "sha512-+sa0XNlWDA6T+bDLmkCUYn6W5k5W6BPRL6mqzSCs6H/xUgtl4D5x2fORKDzopKiU6wsyn/+wXlRXwXeSp+mtoA==",
"requires": {
"semver": "^5.4.1"
}
@ -9503,9 +9499,9 @@
},
"dependencies": {
"@types/node": {
"version": "6.14.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.9.tgz",
"integrity": "sha512-leP/gxHunuazPdZaCvsCefPQxinqUDsCxCR5xaDUrY2MkYxQRFZZwU5e7GojyYsGB7QVtCi7iVEl/hoFXQYc+w=="
"version": "6.14.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.10.tgz",
"integrity": "sha512-pF4HjZGSog75kGq7B1InK/wt/N08BuPATo+7HRfv7gZUzccebwv/fmWVGs/j6LvSiLWpCuGGhql51M/wcQsNzA=="
}
}
},
@ -14484,4 +14480,4 @@
}
}
}
}
}

View File

@ -57,11 +57,10 @@
"bindings": "^1.3.0",
"bluebird": "^3.7.2",
"bootstrap-sass": "^3.3.6",
"color": "^2.0.1",
"d3": "^4.13.0",
"debug": "^3.1.0",
"electron-updater": "4.0.6",
"etcher-sdk": "^3.0.1",
"etcher-sdk": "^4.0.0",
"flexboxgrid": "^6.3.0",
"immutable": "^3.8.1",
"inactivity-timer": "^1.0.0",

View File

@ -28,16 +28,12 @@ describe('Model: flashState', function() {
it('should be able to reset the progress state', function() {
flashState.setFlashingFlag();
flashState.setProgressState({
flashing: 2,
verifying: 0,
successful: 0,
failed: 0,
type: 'flashing',
percentage: 50,
eta: 15,
speed: 100000000000,
averageSpeed: 100000000000,
totalSpeed: 200000000000,
bytes: 0,
position: 0,
active: 0,
@ -46,14 +42,11 @@ describe('Model: flashState', function() {
flashState.resetState();
expect(flashState.getFlashState()).to.deep.equal({
flashing: 0,
verifying: 0,
successful: 0,
active: 0,
failed: 0,
percentage: 0,
speed: null,
averageSpeed: null,
totalSpeed: null,
});
});
@ -100,16 +93,12 @@ describe('Model: flashState', function() {
expect(function() {
flashState.setProgressState({
flashing: 2,
verifying: 0,
successful: 0,
failed: 0,
type: 'flashing',
percentage: 50,
eta: 15,
speed: 100000000000,
averageSpeed: 100000000000,
totalSpeed: 200000000000,
bytes: 0,
position: 0,
active: 0,
@ -121,16 +110,12 @@ describe('Model: flashState', function() {
flashState.setFlashingFlag();
expect(function() {
flashState.setProgressState({
flashing: 2,
verifying: 0,
successful: 0,
failed: 0,
type: 'flashing',
percentage: 0,
eta: 15,
speed: 100000000000,
averageSpeed: 100000000000,
totalSpeed: 200000000000,
bytes: 0,
position: 0,
active: 0,
@ -142,16 +127,12 @@ describe('Model: flashState', function() {
flashState.setFlashingFlag();
expect(function() {
flashState.setProgressState({
flashing: 2,
verifying: 0,
successful: 0,
failed: 0,
type: 'flashing',
percentage: 101,
eta: 15,
speed: 0,
averageSpeed: 0,
totalSpeed: 1,
bytes: 0,
position: 0,
active: 0,
@ -163,16 +144,12 @@ describe('Model: flashState', function() {
flashState.setFlashingFlag();
expect(function() {
flashState.setProgressState({
flashing: 2,
verifying: 0,
successful: 0,
failed: 0,
type: 'flashing',
percentage: -1,
eta: 15,
speed: 0,
averageSpeed: 0,
totalSpeed: 1,
bytes: 0,
position: 0,
active: 0,
@ -184,16 +161,12 @@ describe('Model: flashState', function() {
flashState.setFlashingFlag();
expect(function() {
flashState.setProgressState({
flashing: 2,
verifying: 0,
successful: 0,
failed: 0,
type: 'flashing',
percentage: 50,
eta: 0,
speed: 100000000000,
averageSpeed: 100000000000,
totalSpeed: 200000000000,
bytes: 0,
position: 0,
active: 0,
@ -205,9 +178,6 @@ describe('Model: flashState', function() {
flashState.setFlashingFlag();
expect(function() {
flashState.setProgressState({
flashing: 2,
verifying: 0,
successful: 0,
failed: 0,
type: 'flashing',
percentage: 50,
@ -215,7 +185,6 @@ describe('Model: flashState', function() {
eta: '15',
speed: 100000000000,
averageSpeed: 100000000000,
totalSpeed: 200000000000,
bytes: 0,
position: 0,
active: 0,
@ -228,15 +197,11 @@ describe('Model: flashState', function() {
expect(function() {
// @ts-ignore
flashState.setProgressState({
flashing: 2,
verifying: 0,
successful: 0,
failed: 0,
type: 'flashing',
percentage: 50,
eta: 15,
averageSpeed: 0,
totalSpeed: 1,
bytes: 0,
position: 0,
active: 0,
@ -248,16 +213,12 @@ describe('Model: flashState', function() {
flashState.setFlashingFlag();
expect(function() {
flashState.setProgressState({
flashing: 2,
verifying: 0,
successful: 0,
failed: 0,
type: 'flashing',
percentage: 50,
eta: 15,
speed: 0,
averageSpeed: 0,
totalSpeed: 1,
bytes: 0,
position: 0,
active: 0,
@ -265,61 +226,15 @@ describe('Model: flashState', function() {
}).to.not.throw('Missing flash fields: speed');
});
it('should throw if totalSpeed is missing', function() {
flashState.setFlashingFlag();
expect(function() {
// @ts-ignore
flashState.setProgressState({
flashing: 2,
verifying: 0,
successful: 0,
failed: 0,
type: 'flashing',
percentage: 50,
eta: 15,
speed: 1,
averageSpeed: 1,
bytes: 0,
position: 0,
active: 0,
});
}).to.throw('Missing flash fields: totalSpeed');
});
it('should not throw if totalSpeed is 0', function() {
flashState.setFlashingFlag();
expect(function() {
flashState.setProgressState({
flashing: 2,
verifying: 0,
successful: 0,
failed: 0,
type: 'flashing',
percentage: 50,
eta: 15,
speed: 0,
averageSpeed: 0,
totalSpeed: 0,
bytes: 0,
position: 0,
active: 0,
});
}).to.not.throw('Missing flash fields: totalSpeed');
});
it('should floor the percentage number', function() {
flashState.setFlashingFlag();
flashState.setProgressState({
flashing: 2,
verifying: 0,
successful: 0,
failed: 0,
type: 'flashing',
percentage: 50.253559459485,
eta: 15,
speed: 0,
averageSpeed: 0,
totalSpeed: 1,
bytes: 0,
position: 0,
active: 0,
@ -344,7 +259,6 @@ describe('Model: flashState', function() {
eta: 0,
speed: 0,
averageSpeed: 0,
totalSpeed: 0,
bytes: 0,
position: 0,
active: 0,
@ -357,15 +271,11 @@ describe('Model: flashState', function() {
expect(() => {
flashState.setFlashingFlag();
flashState.setProgressState({
flashing: 0,
verifying: 0,
successful: 0,
failed: 0,
percentage: 0,
eta: 0,
speed: 0,
averageSpeed: 0,
totalSpeed: 0,
bytes: 0,
position: 0,
active: 0,
@ -395,28 +305,21 @@ describe('Model: flashState', function() {
flashState.resetState();
const currentFlashState = flashState.getFlashState();
expect(currentFlashState).to.deep.equal({
flashing: 0,
verifying: 0,
successful: 0,
active: 0,
failed: 0,
percentage: 0,
speed: null,
averageSpeed: null,
totalSpeed: null,
});
});
it('should return the current flash state', function() {
const state = {
flashing: 1,
verifying: 0,
successful: 0,
failed: 0,
percentage: 50,
eta: 15,
speed: 0,
averageSpeed: 0,
totalSpeed: 0,
bytes: 0,
position: 0,
active: 0,
@ -427,15 +330,11 @@ describe('Model: flashState', function() {
flashState.setProgressState(state);
const currentFlashState = flashState.getFlashState();
expect(currentFlashState).to.deep.equal({
flashing: 1,
verifying: 0,
successful: 0,
failed: 0,
percentage: 50,
eta: 15,
speed: 0,
averageSpeed: 0,
totalSpeed: 0,
bytes: 0,
position: 0,
active: 0,
@ -532,30 +431,22 @@ describe('Model: flashState', function() {
flashState.setFlashingFlag();
flashState.setProgressState({
flashing: 2,
verifying: 0,
successful: 0,
failed: 0,
type: 'flashing',
percentage: 50,
eta: 15,
speed: 100000000000,
averageSpeed: 100000000000,
totalSpeed: 200000000000,
bytes: 0,
position: 0,
active: 0,
active: 2,
});
expect(flashState.getFlashState()).to.not.deep.equal({
flashing: 2,
verifying: 0,
successful: 0,
failed: 0,
percentage: 0,
speed: 0,
averageSpeed: 0,
totalSpeed: 0,
});
flashState.unsetFlashingFlag({
@ -564,14 +455,11 @@ describe('Model: flashState', function() {
});
expect(flashState.getFlashState()).to.deep.equal({
flashing: 0,
verifying: 0,
successful: 0,
active: 0,
failed: 0,
percentage: 0,
speed: null,
averageSpeed: null,
totalSpeed: null,
});
});

View File

@ -23,9 +23,8 @@ describe('Browser: progressStatus', function() {
describe('.fromFlashState()', function() {
beforeEach(function() {
this.state = {
flashing: 1,
verifying: 0,
successful: 0,
active: 1,
type: 'flashing',
failed: 0,
percentage: 0,
eta: 15,
@ -42,31 +41,29 @@ describe('Browser: progressStatus', function() {
it('should handle percentage == 0, flashing, unmountOnSuccess', function() {
this.state.speed = 0;
expect(progressStatus.fromFlashState(this.state)).to.equal('Starting...');
expect(progressStatus.fromFlashState(this.state)).to.equal('0% Flashing');
});
it('should handle percentage == 0, flashing, !unmountOnSuccess', function() {
this.state.speed = 0;
settings.set('unmountOnSuccess', false);
expect(progressStatus.fromFlashState(this.state)).to.equal('Starting...');
expect(progressStatus.fromFlashState(this.state)).to.equal('0% Flashing');
});
it('should handle percentage == 0, verifying, unmountOnSuccess', function() {
this.state.speed = 0;
this.state.flashing = 0;
this.state.verifying = 1;
this.state.type = 'verifying';
expect(progressStatus.fromFlashState(this.state)).to.equal(
'Validating...',
'0% Validating',
);
});
it('should handle percentage == 0, verifying, !unmountOnSuccess', function() {
this.state.speed = 0;
this.state.flashing = 0;
this.state.verifying = 1;
this.state.type = 'verifying';
settings.set('unmountOnSuccess', false);
expect(progressStatus.fromFlashState(this.state)).to.equal(
'Validating...',
'0% Validating',
);
});
@ -86,18 +83,16 @@ describe('Browser: progressStatus', function() {
});
it('should handle percentage == 50, verifying, unmountOnSuccess', function() {
this.state.flashing = 0;
this.state.verifying = 1;
this.state.percentage = 50;
this.state.type = 'verifying';
expect(progressStatus.fromFlashState(this.state)).to.equal(
'50% Validating',
);
});
it('should handle percentage == 50, verifying, !unmountOnSuccess', function() {
this.state.flashing = 0;
this.state.verifying = 1;
this.state.percentage = 50;
this.state.type = 'verifying';
settings.set('unmountOnSuccess', false);
expect(progressStatus.fromFlashState(this.state)).to.equal(
'50% Validating',
@ -115,7 +110,7 @@ describe('Browser: progressStatus', function() {
this.state.percentage = 100;
settings.set('validateWriteOnSuccess', false);
expect(progressStatus.fromFlashState(this.state)).to.equal(
'Unmounting...',
'Finishing...',
);
});
@ -129,17 +124,14 @@ describe('Browser: progressStatus', function() {
});
it('should handle percentage == 100, verifying, unmountOnSuccess', function() {
this.state.flashing = 0;
this.state.verifying = 1;
this.state.percentage = 100;
this.state.type = 'verifying';
expect(progressStatus.fromFlashState(this.state)).to.equal(
'Unmounting...',
'Finishing...',
);
});
it('should handle percentage == 100, validatinf, !unmountOnSuccess', function() {
this.state.flashing = 0;
this.state.verifying = 1;
this.state.percentage = 100;
settings.set('unmountOnSuccess', false);
expect(progressStatus.fromFlashState(this.state)).to.equal(

View File

@ -30,9 +30,8 @@ describe('Browser: WindowProgress', function() {
windowProgress.currentWindow.setTitle = this.setTitleSpy;
this.state = {
flashing: 1,
verifying: 0,
successful: 0,
active: 1,
type: 'flashing',
failed: 0,
percentage: 85,
speed: 100,
@ -79,8 +78,7 @@ describe('Browser: WindowProgress', function() {
});
it('should set the verifying title', function() {
this.state.flashing = 0;
this.state.verifying = 1;
this.state.type = 'verifying';
windowProgress.set(this.state);
assert.calledWith(this.setTitleSpy, ' 85% Validating');
});
@ -89,7 +87,7 @@ describe('Browser: WindowProgress', function() {
this.state.percentage = 0;
this.state.speed = 0;
windowProgress.set(this.state);
assert.calledWith(this.setTitleSpy, ' Starting...');
assert.calledWith(this.setTitleSpy, ' 0% Flashing');
});
it('should set the finishing title', function() {