Files
Aislo/resources/tester/test_est_quantity_move_export.py
T

112 lines
7.7 KiB
Python

import json
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
def test_reorder_preserves_marker_structure_and_rejects_late_styles():
script = r"""const w=require('./tools/est-test/export-quantity.cjs'),q=require('./tools/est-test/native-quantity.cjs');
const div=(tag,code,value)=>'<div cs=1><c nr=1/><begin Tag='+tag+' style="Name:&#39;'+code+'&#39;;"/>'+value+'<end Tag='+tag+'/></div>';
const doc='<cs nr=1/>'+div(1,'r000001','1')+div(2,'r000002','2');
const sheet=q.fromWpt('N','sheet',doc,[],{}),rows=[sheet.rows[1],sheet.rows[0]],out=w.reorderRows(doc,sheet,rows);
const parsed=q.fromWpt('N','sheet',out.text,[],{});let rejected=false;
const late='<cs nr=1/>'+div(1,'r000001','1')+'<cs nr=2/><div cs=2>2</div>';
const other=q.fromWpt('N','sheet',late,[],{});
try{w.reorderRows(late,other,[other.rows[1],other.rows[0]])}catch{rejected=true}
const redefined='<cs nr=1/>'+div(1,'r000001','1')+'<cs nr=1/>'+div(2,'r000002','2');
const third=q.fromWpt('N','sheet',redefined,[],{});let changed=false;
try{w.reorderRows(redefined,third,[third.rows[1],third.rows[0]])}catch{changed=true}
process.stdout.write(JSON.stringify({rows:JSON.stringify(parsed.rows)===JSON.stringify(rows),rejected,changed}));"""
result = json.loads(subprocess.check_output(["node", "-e", script], cwd=ROOT))
assert result == {"rows": True, "rejected": True, "changed": True}
def test_append_handles_many_existing_tag_numbers():
script = r"""const w=require('./tools/est-test/export-quantity.cjs');
const doc='<cs nr=1/>'+Array.from({length:150000},(_,i)=>'<end Tag='+i+'/>').join('');
const row={LineData:'1=\x120\x12',markers:['r000001']};
const out=w.appendRows(doc,{items:[{Code:'r000001'}]},[row]);
process.stdout.write(JSON.stringify(out.includes('<begin Tag=150000 ')));"""
assert json.loads(subprocess.check_output(["node", "-e", script], cwd=ROOT, timeout=15))
def test_move_checks_shared_tags_without_confusing_similar_numbers():
script = r"""const w=require('./tools/est-test/export-quantity.cjs'),q=require('./tools/est-test/native-quantity.cjs');
const div=(tag,code)=>'<div cs=1><c nr=1/><begin Tag='+tag+' style="Name:&#39;'+code+'&#39;;"/>1<end Tag='+tag+'/></div>';
const noise=Array.from({length:10000},(_,i)=>'<end Tag='+(i+100)+'/>').join('');
const doc='<cs nr=1/>'+noise+div(1,'r000001')+div(10,'r000002');
const sheet=q.fromWpt('N','sheet',doc,[],{}),rows=[sheet.rows[1],sheet.rows[0]];
const output=w.reorderRows(doc,sheet,rows);let rejected=false;
try{w.reorderRows(doc+'<end Tag=1/>',sheet,rows)}catch{rejected=true}
process.stdout.write(JSON.stringify({rows:JSON.stringify(q.fromWpt('N','sheet',output.text,[],{}).rows)===JSON.stringify(rows),rejected}));"""
result = json.loads(subprocess.check_output(["node", "-e", script], cwd=ROOT, timeout=30))
assert result == {"rows": True, "rejected": True}
def test_row_parser_preserves_global_and_local_tags_with_many_rows():
script = r"""const q=require('./tools/est-test/native-quantity.cjs'),{rowParser}=require('./tools/est-test/wpt-parse.cjs');
const one=(tag,code)=>'<div><c nr=2/><begin Tag='+tag+' style="Name:&#39;'+code+'&#39;;"/>1<end Tag='+tag+'/></div>';
const styles=Array.from({length:3000},(_,i)=>'<cs nr='+(i+2)+' CharStyleON:0/>').join('');
const doc='<cs nr=2 CharStyleON:16/>'+styles+Array.from({length:3000},(_,i)=>one(i,'r'+String(i).padStart(6,'0'))).join('')+
'<div><end Tag=1/>2</div>'+one(1,'r009999');
const declarations=[...doc.matchAll(/<(?:cs|begin)\b[^>]*>/gi)].map(m=>m[0]).join('');
const divs=[...doc.matchAll(/<div\b[^>]*>[\s\S]*?<\/div>/gi)].map(m=>m[0]),parse=rowParser(doc);
const samples=[divs[0],divs[1],divs[100],divs.at(-2),divs.at(-1)];
const same=samples.every(div=>JSON.stringify(parse(div))===JSON.stringify(q.fromWpt('','',declarations+div,[],{}).rows));
const count=divs.reduce((n,div)=>n+parse(div).length,0);
process.stdout.write(JSON.stringify({same,count}));"""
result = json.loads(subprocess.check_output(["node", "-e", script], cwd=ROOT, timeout=30))
assert result == {"same": True, "count": 3002}
def test_move_and_edit_keeps_original_row_marker():
script = r"""const w=require('./tools/est-test/export-quantity.cjs'),q=require('./tools/est-test/native-quantity.cjs');
const div=(tag,code,value)=>'<div cs=1><c nr=1/>Q='+value+'=<begin Tag='+tag+' style="Name:&#39;'+code+'&#39;;"/>'+value+'<end Tag='+tag+'/></div>';
const doc='<cs nr=1/>'+div(1,'r000001','1')+div(2,'r000002','2');
const sheet=q.fromWpt('N','sheet',doc,[],{}),rows=structuredClone([sheet.rows[1],sheet.rows[0]]);
rows[0].LineData=rows[0].LineData.replace('Q=2','Q=3');
const moved=w.reorderRows(doc,sheet,rows),old={...sheet,rows:moved.rows},edited={...sheet,rows};
const result=w.editDocument(moved.text,old,edited),parsed=q.fromWpt('N','sheet',result,[],{});
process.stdout.write(JSON.stringify(parsed.rows));"""
result = json.loads(subprocess.check_output(["node", "-e", script], cwd=ROOT))
assert result[0]["markers"] == ["r000002"]
assert result[0]["LineData"].startswith("Q=3")
assert result[1]["markers"] == ["r000001"]
def test_move_quantity_rows_in_installed_file_when_sample_exists():
sample = Path("C:/Dasansoft/Est Plus 2/Data/ProjectData/main.estx")
if not sample.exists():
pytest.skip("installed EST sample unavailable")
script = r"""const fs=require('fs'),z=require('zlib'),m=require('./tools/est-test/import-estx.cjs'),
e=require('./tools/est-test/export-estx.cjs'),w=require('./tools/est-test/export-quantity.cjs'),c=require('./tools/est-test/quantity-calculate.cjs');
const bytes=fs.readFileSync(process.argv[1]),p=m.importEstx(bytes);let candidate=null;
const valid=s=>{const x=c.calculateQuantity(s),n=s.rows.reduce((a,r)=>a+r.markers.length,0);
return !x.errors.length&&x.checks.length===n&&x.checks.every(v=>Number.isFinite(v.stored)&&v.stored===v.calculated)&&x.items.every((v,i)=>v.GN===s.items[i].GN)};
for(const s of p.quantitySheets){if(!valid(s))continue;const r=p.tables.MulMainDat.find(x=>x.Code===s.Code);
let b=Buffer.from(r.Editor.binary,'base64');if(b[0]===120)b=z.inflateSync(b);const doc=new TextDecoder('euc-kr').decode(b);
for(let i=1;i<s.rows.length;i++){if(s.rows[i-1].markers.length!==1||s.rows[i].markers.length!==1)continue;
const rows=s.rows.slice();[rows[i-1],rows[i]]=[rows[i],rows[i-1]];
if(JSON.stringify(rows)===JSON.stringify(s.rows))continue;
rows[i-1]={...rows[i-1],LineData:' '+rows[i-1].LineData};
try{const result=w.reorderRows(doc,s,rows);if(result.text===doc||!valid({...s,rows}))continue;
w.editDocument(result.text,{...s,rows:result.rows},{...s,rows});
candidate={sheet:s,rows};break}catch{}}
if(candidate)break;
}
if(!candidate)throw Error('no safe move fixture');
const s=candidate.sheet,before=structuredClone(s);s.rows=candidate.rows;
const out=e.exportEstx(p),q=m.importEstx(out),saved=q.quantitySheets.find(x=>x.Code===s.Code),a=m.unzip(bytes),b=m.unzip(out);
process.stdout.write(JSON.stringify({rows:JSON.stringify(saved.rows)===JSON.stringify(s.rows),
items:JSON.stringify(saved.items)===JSON.stringify(before.items),
indexes:[...a].filter(([name])=>name.endsWith('.idx')).every(([name,data])=>data.equals(b.get(name)))}));"""
result = json.loads(
subprocess.check_output(["node", "-e", script, str(sample)], cwd=ROOT, timeout=90).decode(
"utf-8"
)
)
assert result == {"rows": True, "items": True, "indexes": True}