├── .gitignore ├── Tests ├── hello.txt ├── files │ ├── qNGET.aplf │ ├── NS1.dyalog │ ├── array.csv │ ├── foo.aplf │ ├── NS0.dyalog │ ├── MyArray.apla │ ├── var.apla │ ├── NonParametric-9.apln │ ├── goodnessOfFit-500.aplo │ ├── london1.txt │ └── london2.ff ├── lx.dws ├── test_SuccessValue0.aplf ├── test_SuccessValue42.aplf ├── test_SuccessValueOK.aplf ├── fail.dyalog ├── teardown.dyalog ├── CompCheck │ ├── CompCheck.url │ ├── avuClassic121.aplf │ ├── README.md │ ├── Run.aplf │ └── Features.aplf ├── test_wsfull1.dyalog ├── test_failsOn180.dyalog ├── test_dfn.aplf ├── nosource │ ├── SourceHasBlanks.aplf │ ├── test_nosource1.dyalogbuild │ └── test_nosource0.dyalogbuild ├── fail.dyalogtest ├── failsOn180.dyalogtest ├── SuccessValue42.dyalogtest ├── SuccessValue0.dyalogtest ├── SuccessValueOK.dyalogtest ├── test_SuccessValueLongString.aplf ├── test_Coverage.dyalogtest ├── test_Coverage_.aplf ├── test_Coverage_main.aplf ├── SuccessValueLongString.dyalogtest ├── SuccessValueEnvVar.dyalogtest ├── SuccessValueEnvVar0.dyalogtest ├── SuccessValueEnvVar42.dyalogtest ├── SuccessValueEnvVarOK.dyalogtest ├── setup_Coverage.dyalog ├── test_SuccessValueFromEnv.aplf ├── DBuild_2.dyalogbuild ├── test_Compatibility.dyalog ├── test_DBuild_nameclash.dyalog ├── DBuild_WSFULL.dyalogbuild ├── test_CompTools.aplf ├── test_retcode.dyalog ├── DBuild_1.dyalogbuild ├── DBuild_3.dyalogbuild ├── DBuild_nameclash.dyalogbuild ├── DBuild_4.dyalogbuild ├── test_assert.aplf ├── test_DSL.dyalog ├── test_nousource.aplf ├── sub_RunAPLProcess.aplf ├── test_DBuild_4.dyalog ├── test_Quiet.dyalog ├── test_DBuild_2.dyalog ├── DTest.dyalogtest ├── test_Coverage.dyalog ├── test_WSFULL.dyalog ├── test_DBuild_tube.dyalog ├── test_DBuild_3.dyalog ├── test_DBuild_1.dyalog ├── test_ReturnCode.dyalog └── test_SuccessValue.dyalog ├── README.md ├── CITA.json5 ├── LICENSE └── DyalogBuild.dyalog /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.rng.txt 3 | -------------------------------------------------------------------------------- /Tests/hello.txt: -------------------------------------------------------------------------------- 1 | h∊llo 2 | w∘rld 3 | -------------------------------------------------------------------------------- /Tests/files/qNGET.aplf: -------------------------------------------------------------------------------- 1 | qNGET←{⎕NGET ⍵ 1} 2 | -------------------------------------------------------------------------------- /Tests/files/NS1.dyalog: -------------------------------------------------------------------------------- 1 | :namespace MyNS1 2 | 3 | src←'MyNS1' 4 | :endnamespace -------------------------------------------------------------------------------- /Tests/files/array.csv: -------------------------------------------------------------------------------- 1 | 1 A,1 B,1 C,1 D 2 | 2 A,2 B,2 C,2 D 3 | 3 A,3 B,3 C,3 D 4 | -------------------------------------------------------------------------------- /Tests/lx.dws: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dyalog/DBuildTest/master/Tests/lx.dws -------------------------------------------------------------------------------- /Tests/test_SuccessValue0.aplf: -------------------------------------------------------------------------------- 1 | test_SuccessValue0←{ 2 | 0=1: 1 3 | 0 4 | } 5 | -------------------------------------------------------------------------------- /Tests/test_SuccessValue42.aplf: -------------------------------------------------------------------------------- 1 | test_SuccessValue42←{ 2 | 0=1: 1 3 | 42 4 | } 5 | -------------------------------------------------------------------------------- /Tests/files/foo.aplf: -------------------------------------------------------------------------------- 1 | R←foo sink;you 2 | ⍝ dummy fn 3 | you←+/?10 10 4 | R←you*2 5 | -------------------------------------------------------------------------------- /Tests/test_SuccessValueOK.aplf: -------------------------------------------------------------------------------- 1 | test_SuccessValueOK←{ 2 | 0=1: 1 3 | 'OK' 4 | } 5 | -------------------------------------------------------------------------------- /Tests/fail.dyalog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dyalog/DBuildTest/master/Tests/fail.dyalog -------------------------------------------------------------------------------- /Tests/teardown.dyalog: -------------------------------------------------------------------------------- 1 | R←teardown sink 2 | ⍝ just reset ⎕IO and⎕ML 3 | #.(⎕io←⎕ml←1) 4 | R←'' -------------------------------------------------------------------------------- /Tests/CompCheck/CompCheck.url: -------------------------------------------------------------------------------- 1 | [InternetShortcut] 2 | URL=https://github.com/mbaas2/compcheck 3 | -------------------------------------------------------------------------------- /Tests/test_wsfull1.dyalog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dyalog/DBuildTest/master/Tests/test_wsfull1.dyalog -------------------------------------------------------------------------------- /Tests/test_failsOn180.dyalog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dyalog/DBuildTest/master/Tests/test_failsOn180.dyalog -------------------------------------------------------------------------------- /Tests/test_dfn.aplf: -------------------------------------------------------------------------------- 1 | test_dfn←{ 2 | 42 Check 42:'This should not happen!' 3 | '' 4 | 5 | } 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/files/NS0.dyalog: -------------------------------------------------------------------------------- 1 | :namespace MyNS0 2 | src←'MyNS0' 3 | ∇ r ← foo r 4 | r ← ⌸ 1 5 | ∇ 6 | :endnamespace -------------------------------------------------------------------------------- /Tests/nosource/SourceHasBlanks.aplf: -------------------------------------------------------------------------------- 1 | R ← a SourceHasBlanks ( b c d );x;y;z 2 | R ← 1 + 2 + a + b + c + d -------------------------------------------------------------------------------- /Tests/fail.dyalogtest: -------------------------------------------------------------------------------- 1 | DyalogTest : 1.30 2 | ID : fail 3 | Description: This is a simple failing test 4 | 5 | 6 | Test : fail 7 | -------------------------------------------------------------------------------- /Tests/failsOn180.dyalogtest: -------------------------------------------------------------------------------- 1 | DyalogTest : 1.30 2 | ID : failsOn18.0 3 | Description: fails on v18.0 4 | 5 | Test : test_failsOn180 6 | -------------------------------------------------------------------------------- /Tests/files/MyArray.apla: -------------------------------------------------------------------------------- 1 | [ 1 2 3 2 | 4 [1 2 3 3 | 4 5 6 4 | 7 8 9] 6 5 | 7 8 9 ] 6 | -------------------------------------------------------------------------------- /Tests/SuccessValue42.dyalogtest: -------------------------------------------------------------------------------- 1 | DyalogTest : 1.70 2 | ID : SuccessValue42 3 | Description: 4 | SuccessValue: 42 5 | Test : test_SuccessValue42 6 | -------------------------------------------------------------------------------- /Tests/SuccessValue0.dyalogtest: -------------------------------------------------------------------------------- 1 | DyalogTest : 1.70 2 | ID : SuccessValue0 3 | Description: 4 | SuccessValue: 0 5 | 6 | Test : test_SuccessValue0 7 | -------------------------------------------------------------------------------- /Tests/SuccessValueOK.dyalogtest: -------------------------------------------------------------------------------- 1 | DyalogTest : 1.70 2 | ID : SuccessValueOK 3 | Description: 4 | SuccessValue: 'OK' 5 | 6 | Test : test_SuccessValueOK 7 | -------------------------------------------------------------------------------- /Tests/test_SuccessValueLongString.aplf: -------------------------------------------------------------------------------- 1 | test_SuccessValueLongString←{ 2 | ',ZI4,<->,ZI2,<->,ZI2,< found no problems!>'⎕fmt 1 3⍴⎕TS 3 | } 4 | -------------------------------------------------------------------------------- /Tests/test_Coverage.dyalogtest: -------------------------------------------------------------------------------- 1 | DyalogTest : 1.70 2 | ID : test_Coverage 3 | Test: test_Coverage_main 4 | setup: setup_Coverage 5 | codecoverage_subject: #.ns_Coverage 6 | -------------------------------------------------------------------------------- /Tests/nosource/test_nosource1.dyalogbuild: -------------------------------------------------------------------------------- 1 | DYALOG BUILD:1.84 2 | ID: foo, Version=1 3 | 4 | 5 | APL: ./*.aplf 6 | EXEC: #.⎕fx'R ← fnWithBlanks C' ' R ← 1 + 1 ' 7 | 8 | TARGET: ./foo ,save=1,nosource=1 -------------------------------------------------------------------------------- /Tests/test_Coverage_.aplf: -------------------------------------------------------------------------------- 1 | R←test_Coverage_ null 2 | R←'' 3 | {}'sample "boilerplate" fn which will be replicated 100 times by setup_Coverage.dyalog' 4 | {}'these lines just help to make the tests less boring' 5 | -------------------------------------------------------------------------------- /Tests/test_Coverage_main.aplf: -------------------------------------------------------------------------------- 1 | R←test_Coverage_main null;trg;i 2 | R←'' 3 | trg←1↑2⊃⎕VFI 1⊃160⌶'COVERAGE_TARGET' 4 | :For i :In ⍳trg 5 | {}#.ns_Coverage⍎'test_Coverage_',(,'ZI4'⎕fmt i),' ⍬' 6 | :EndFor 7 | -------------------------------------------------------------------------------- /Tests/SuccessValueLongString.dyalogtest: -------------------------------------------------------------------------------- 1 | DyalogTest : 1.70 2 | ID : SuccessValueLongString ⍝ will be passed on commandline to also test decoding 3 | Description: 4 | 5 | Test : test_SuccessValueLongString 6 | -------------------------------------------------------------------------------- /Tests/nosource/test_nosource0.dyalogbuild: -------------------------------------------------------------------------------- 1 | DYALOG BUILD:1.84 2 | ID: foo, Version=1 3 | 4 | APL: ./*.aplf,TARGET=# 5 | EXEC: 2#.⎕fix'R ← fnWithBlanks C' ' R ← 1 + 1 ' 6 | 7 | TARGET: ./foo ,save=1,nosource=0 -------------------------------------------------------------------------------- /Tests/SuccessValueEnvVar.dyalogtest: -------------------------------------------------------------------------------- 1 | DyalogTest : 1.70 2 | ID : SuccessValue 3 | Description: 4 | SuccessValue: 0 ⍝ we always expect 0, but test will return result given in EnvVar "SucVal" 5 | 6 | Test : test_SuccessValueFromEnv 7 | -------------------------------------------------------------------------------- /Tests/SuccessValueEnvVar0.dyalogtest: -------------------------------------------------------------------------------- 1 | DyalogTest : 1.70 2 | ID : SuccessValueEnvVar0 3 | Description: 4 | SuccessValue: json!0 ⍝ we always expect 0, but test will return result given in EnvVar "SucVal" 5 | 6 | Test : test_SuccessValueFromEnv 7 | -------------------------------------------------------------------------------- /Tests/SuccessValueEnvVar42.dyalogtest: -------------------------------------------------------------------------------- 1 | DyalogTest : 1.70 2 | ID : SuccessValueEnvVar42 3 | Description: 4 | SuccessValue: 42 ⍝ we always expect 0, but test will return result given in EnvVar "SucVal" 5 | 6 | Test : test_SuccessValueFromEnv 7 | -------------------------------------------------------------------------------- /Tests/SuccessValueEnvVarOK.dyalogtest: -------------------------------------------------------------------------------- 1 | DyalogTest : 1.70 2 | ID : SuccessValueEnvVarOK 3 | Description: 4 | SuccessValue: OK ⍝ we always expect 0, but test will return result given in EnvVar "SucVal" 5 | 6 | Test : test_SuccessValueFromEnv 7 | -------------------------------------------------------------------------------- /Tests/setup_Coverage.dyalog: -------------------------------------------------------------------------------- 1 | r←setup_Coverage null;nr;n;NR 2 | '#.ns_Coverage'⎕NS'' 3 | nr←⎕NR'test_Coverage_' 4 | :For n :In ⍳100 5 | NR←nr 6 | NR[1]←⊂'R←test_Coverage_',(,'ZI4'⎕fmt n),' null' 7 | #.ns_Coverage.⎕FX NR 8 | :EndFor 9 | r←'' 10 | -------------------------------------------------------------------------------- /Tests/test_SuccessValueFromEnv.aplf: -------------------------------------------------------------------------------- 1 | test_SuccessValueFromEnv←{ 2 | { 3 | v←⍵ 4 | 0=≢v:v 5 | 0::v⊣⎕←⎕dmx 6 | 'b64!'≡4↑v:∇ ⎕se._cita.base64dec 4↓v 7 | 'json!'≡5↑v:∇ 0 ⎕JSON 5↓v 8 | 'apl!'≡4↑v:∇⍎4↓v 9 | v 10 | }2 ⎕NQ'.' 'GetEnvironment' 'SucVal' 11 | } 12 | -------------------------------------------------------------------------------- /Tests/DBuild_2.dyalogbuild: -------------------------------------------------------------------------------- 1 | DYALOGBUILD: 1.44 2 | ID: BuildTest2 3 | ⍝ requires v17.1 or greater (because of .apla files) - test_DBuild2.dyalog takes care of that 4 | 5 | APL: ./files/*.apln 6 | DATA: ./files/*.apla 7 | DATA: ./files/london1*.txt 8 | DATA: ./files/london2.ff, format=charvec, SetEOL=ff 9 | CSV: ./files/array.csv, Target=array -------------------------------------------------------------------------------- /Tests/test_Compatibility.dyalog: -------------------------------------------------------------------------------- 1 | r←test_Compatibility sink 2 | r←'' 3 | 4 | ⎕SE.SALT.Load(∊1 ⎕NPARTS ##.TESTSOURCE,'../Tests/CompCheck/*.aplf'),' -target=#' ⍝ load CompCheck utility 5 | 6 | :If 1 Check(18 #.Run ##.TESTSOURCE,'../DyalogBuild.dyalog') ⍝ what could go wrong? 7 | →0 Because'Test for compatibility with v18.0 failed' ⋄ :EndIf -------------------------------------------------------------------------------- /Tests/test_DBuild_nameclash.dyalog: -------------------------------------------------------------------------------- 1 | r←test_DBuild_nameclash dummy;res;args 2 | r←'' 3 | ⎕se.SALTUtils.DEBUG←0 4 | res←##.Build args←##.TESTSOURCE,'DBuild_nameclash.dyalogbuild -c -q=2' 5 | 6 | :If 1 Check∨/'6 errors encountered.'⍷∊res 7 | →0 Because'DBuild did not fail with 4 errors while executing...',(⎕UCS 13),']DBuild ',args ⋄ :EndIf 8 | -------------------------------------------------------------------------------- /Tests/DBuild_WSFULL.dyalogbuild: -------------------------------------------------------------------------------- 1 | DYALOGBUILD: 1.3 2 | ID: DBuild_WSFULL_1, Version=1 (for Dyalog 12 and better) 3 | EXEC: ⎕ex¨'big'∘,¨'1234' 4 | EXEC: wa←⎕wa 5 | EXEC: ⎕DL 1 ⍝ delay a bit to allow caller to catch up 6 | EXEC: big1 ←(⌈.1×wa)⍴3.11 7 | EXEC: big2 ←(⌈.2×wa)⍴3.12 8 | EXEC: big3 ←(⌈.3×wa)⍴3.13 9 | EXEC: big4 ←(⌈.4×wa)⍴3.14 ⍝ use up WA... 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DBuild 2 | 3 | DBuild is a tool to build a distributeable version of your code (currently: a workspace) from source-code in files. DTest supports unit tests. More doc in the [wiki](https://github.com/Dyalog/DBuildTest/wiki). 4 | The only file that is required to run DBuild or DTest is DBuildTest.dyalog, the file CITA.json5 is used to manage the testing of the repository. 5 | 6 | -------------------------------------------------------------------------------- /Tests/test_CompTools.aplf: -------------------------------------------------------------------------------- 1 | r←test_CompTools 2 | ⍝ test the various compatibility tools...(which are also important to allow running DTest on all versions!) 3 | r←'' 4 | (⊂hw←'h∊llo' 'w∘rld')##.⎕NPUT(fl←##.TESTSOURCE,'/hello.txt')1 ⍝ test against UTF-8 data 5 | 6 | :If hw Check 2↑⊃##.qNGET fl 7 | →fail Because'qNGET did not return what ⎕NPUT wrote!' ⋄ :EndIf 8 | 9 | 10 | →0 11 | fail: 12 | -------------------------------------------------------------------------------- /Tests/test_retcode.dyalog: -------------------------------------------------------------------------------- 1 | r←test_retcode dummy;ok 2 | ⍝ useful testcase in combination with -off to test returncode 3 | ⍝ use "ok=0|1" to control result of this test (default is ok) 4 | ⍝ This test is used by test_ReturnCode. 5 | r←'' 6 | ok←⊃1↑(1+⎕io)⊃⎕vfi{0=⍴,⍵:'1'⋄⍵}2⎕nq'.' 'GetEnvironment' 'ok' 7 | :if 1 Check ok 8 | →0 Because 'This test was instructed to fail by passing envvar "ok" with value ',(⍕ok),'!' ⋄ :endif -------------------------------------------------------------------------------- /Tests/DBuild_1.dyalogbuild: -------------------------------------------------------------------------------- 1 | DYALOGBUILD: 1.7 2 | ID: DBuild_TEST_2, Version 3 (for Dyalog 18 and better) 3 | 4 | DEFAULTS: ⎕IO←⎕ml←0 5 | NS: ./files/NS0.dyalog, target=#.MyNS0 6 | DEFAULTS: ⎕IO←⎕ml←1 7 | NS: ./files/NS1.dyalog, target=#.MyNS1 8 | 9 | DEFAULTS: ⎕IO←0 ⋄ ⎕ML←2 10 | DEFAULTS: ⎕CT←1E¯11 ⋄ ⎕PP←11 11 | 12 | EXEC: #.ProdFlag←'Test' 13 | EXEC: #.MyEnvVar←'${DB_EnvVar}' 14 | EXEC: #.Dollar←'$$foo' 15 | 16 | PROD: #.ProdFlag←'Production' 17 | 18 | 19 | TARGET: /tmp/foo,save=1 20 | -------------------------------------------------------------------------------- /Tests/DBuild_3.dyalogbuild: -------------------------------------------------------------------------------- 1 | DYALOGBUILD: 1.44 2 | ID: DBuild_TEST_3, Version 2 (needs v17.1 or better) 3 | 4 | DEFAULTS: ⎕IO←⎕ml←0 5 | NS: ./files/NS0.dyalog, target=MyNS0 6 | DEFAULTS: ⎕IO←⎕ml←1 7 | NS: ./files/NS1.dyalog, target=MyNS1 8 | 9 | DEFAULTS: ⎕IO←0 ⋄ ⎕ML←2 10 | DEFAULTS: ⎕CT←1E¯11 ⋄ ⎕PP←11 11 | 12 | LIB: InitConga, Target=#.conga 13 | LIB: HttpCommand, Target=#.httpcommand 14 | 15 | EXEC: #.ProdFlag←'Test' 16 | EXEC: #.MyEnvVar←'%DB_EnvVar%' 17 | EXEC: #.Dollar←'$$foo' 18 | 19 | PROD: #.ProdFlag←'Production' -------------------------------------------------------------------------------- /Tests/files/var.apla: -------------------------------------------------------------------------------- 1 | [ ( ( (1) 2 | 'A') 3 | ( (1) 4 | 'B') 5 | ( (1) 6 | 'C') 7 | ( (1) 8 | ('D') ) ) 9 | ( ( (2) 10 | 'A') 11 | ( (2) 12 | 'B') 13 | ( (2) 14 | 'C') 15 | ( (2) 16 | ('D') ) ) 17 | ( ( (3) 18 | 'A') 19 | ( (3) 20 | 'B') 21 | ( (3) 22 | 'C') 23 | ( (3) 24 | ('D') ) ) ] 25 | -------------------------------------------------------------------------------- /Tests/DBuild_nameclash.dyalogbuild: -------------------------------------------------------------------------------- 1 | DYALOGBUILD: 1.3 2 | ID: NameClash 3 | 4 | ⍝ this test creates a few conflicting names - so we want to check if DBuild reports them! 5 | 6 | EXEC: #.NameClash1←⍳3 7 | NS: ./files/NS1*.dyalog, target=#.NameClash1 ⍝ error 1: collision of existing var name & target 8 | 9 | EXEC: #.NameClash2←⍳3 10 | CSV: ./files/array.csv, Target=#.NameClash2 ⍝ error 2: another collision 11 | 12 | CSV: ./files/array.csv, Target=#.NameClash3 13 | NS: ./files/NS1*.dyalog, target=#.NameClash3 ⍝ the 3d one 14 | 15 | EXEC: #.london1←⍳4 16 | DATA: ./files/london1*.txt, target=# ⍝ and this should also not work... 17 | -------------------------------------------------------------------------------- /Tests/DBuild_4.dyalogbuild: -------------------------------------------------------------------------------- 1 | DYALOGBUILD: 1.44 2 | ID: DBuild_TEST_4 3 | 4 | DEFAULTS: ⎕IO←⎕ml←0 5 | NS: ./files/NS0.dyalog, target=MyNS0 6 | DEFAULTS: ⎕IO←⎕ml←1 7 | NS: ./files/NS1.dyalog, target=MyNS1 8 | 9 | DEFAULTS: ⎕IO←0 ⋄ ⎕ML←2 10 | DEFAULTS: ⎕CT←1E¯11 ⋄ ⎕PP←11 11 | 12 | LIB: InitConga, Target=#.conga 13 | LIB: HttpCommand, Target=#.httpcommand 14 | 15 | EXEC: #.ProdFlag←'Test' 16 | EXEC: #.MyEnvVar←'$DB_EnvVar' 17 | EXEC: #.Dollar←'$$foo' 18 | 19 | LX: '⎕←''Hello World!''' 20 | 21 | TARGET: /tmp/DBuild_TEST_4.exe, type = NativeExe,details=CompanyName:Dyalog Ltd.;Comments: This is a sample build;FileVersion:3.14;FileDescription:just a sample;InternalName:MyTest;ProductName:DyalogBuild 22 | TARGET: /tmp/DBuild_TEST_4,save=1 -------------------------------------------------------------------------------- /Tests/test_assert.aplf: -------------------------------------------------------------------------------- 1 | test_assert←{⍝ this test shows how to write tests a dfns 2 | 3 | ⍝ it is a sample - but testing the Assert function will require calling this or other tests with DTest and checking we get the right results! 4 | ⍝ as done in test_SuccessValue.dyalog 5 | 6 | 7 | ⍝ this should not fail with value 0.5+0.5! 8 | sink←1 Assert 0.5+0.5 9 | 10 | 1 Assert 0*0: ⍝ this comment could explain a reason for the failure 11 | 12 | MyMsg←'Error msg computed elsewhere...' ⍝ result of difficult computations! 13 | MyMsg⊢1 Assert 2×0.5: ⍝this is syntax if the msg can not be given in comment, but is result of some other routine 14 | 15 | 1 Check 1: MyMsg ⍝ "traditional" checks return an error msg if test fails 16 | 17 | 0 ⍝ empty string as default, indicating "no errors to report" 18 | } 19 | -------------------------------------------------------------------------------- /CITA.json5: -------------------------------------------------------------------------------- 1 | /******************************************************************************************** 2 | 3 | This file is only needed by CITA - Continous Integration Testing in APL 4 | It's not needed to USE DBuild/DTest - just to TEST it with CITA. 5 | 6 | ********************************************************************************************/ 7 | { 8 | secondstimeout: 900, 9 | tests: [{ 10 | "DyalogVersions": "18-", 11 | "Test": "./Tests/DTest.dyalogtest", 12 | "EMail": [{ 13 | "All": "mbaas@dyalog.com" 14 | }], 15 | id:"All" 16 | }, 17 | { 18 | id: "Failing", 19 | Test: "./Tests/fail.dyalogtest", 20 | disabled: true 21 | }, 22 | { 23 | "id":"Fail180", 24 | Test: "./Tests/failsOn180.dyalogtest", 25 | disabled: true 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /Tests/files/NonParametric-9.apln: -------------------------------------------------------------------------------- 1 | :Namespace NonParametric 2 | 3 | 4 | 5 | 6 | 7 | 8 | signTest←{ 9 | ⍝ Written by Steve Mansour 2020 10 | ⍝∇ Perform sign Test 11 | ⍝⍺ Significance Level (Default 0.05) 12 | ⍝⍵ Vector of Differences or Two Vectors or Matrix 13 | ⍝← Namespace 14 | ⍝NS←.95 signTest BEFORE AFTER 15 | ⎕io←0 ⋄ ⎕ml←3 16 | ⍺←0.05 ⋄ NS←⎕NS'' 17 | D←⊃-/⊆⍣(1=↑⍴⍴⍵)⊢⍵ ⍝ Differences 18 | FR←+/¯1 1∘.=×⍵ ⍝ Frequency 19 | X N←(⌊/,+/)FR ⍝ Less frequent sign occurances 20 | Z←N{⍺>25:(1-⍺-2×⍵)÷⍺*÷2 ⋄ ⍵}X ⍝ Test Statistic 21 | CV←N 0.5 binomial criticalValue>⍺ ⍝ Critical Value 22 | P←N{⍺>25:normal prob≥⍵ ⍝ P-Value 23 | ⍺ 0.5 binomial prob≥⍵}Z 24 | NS.TestStatistic←Z 25 | NS.CriticalValue←CV 26 | NS.P←P 27 | NS.Significance←⍺ 28 | NS 29 | } 30 | 31 | 32 | 33 | 34 | 35 | :EndNamespace 36 | -------------------------------------------------------------------------------- /Tests/CompCheck/avuClassic121.aplf: -------------------------------------------------------------------------------- 1 | R←avuClassic131 2 | ⍝ ⎕AVU from Classic 13.1 3 | R← 0 8 10 13 32 12 6 7 27 9 9014 619 37 39 9082 9077 95 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 1 2 175 46 9068 48 49 50 51 52 53 54 55 56 57 3 8866 165 36 163 162 8710 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 4 5 253 183 127 9049 193 194 195 199 200 202 203 204 205 206 207 208 210 211 212 213 217 218 219 221 254 227 236 240 242 245 123 8364 125 8867 9015 168 192 196 197 198 9064 201 209 214 216 220 223 224 225 226 228 229 230 231 232 233 234 235 237 238 239 241 91 47 9023 92 9024 60 8804 61 8805 62 8800 8744 8743 45 43 247 215 63 8714 9076 126 8593 8595 9075 9675 42 8968 8970 8711 8728 40 8834 8835 8745 8746 8869 8868 124 59 44 9073 9074 9042 9035 9033 9021 8854 9055 9017 33 9045 9038 9067 9066 8801 8802 243 244 246 248 34 35 30 38 180 9496 9488 9484 9492 9532 9472 9500 9508 9524 9516 9474 64 249 250 251 94 252 96 8739 182 58 9079 191 161 8900 8592 8594 9053 41 93 31 160 167 9109 9054 9059 4 | 5 | 6 | -------------------------------------------------------------------------------- /Tests/test_DSL.dyalog: -------------------------------------------------------------------------------- 1 | r←test_DSL dummy;v1;halt∆ 2 | ⍝ some vary basic tests to make sure that the DSL works as expected 3 | r←'' 4 | 5 | :If ##._OS Check∊##.(_isMacOS,_isWin,_isLinux)/'Mac' 'Win' 'Lin' 6 | →0 Because'Flags for OS did not match SALT''s opinion' ⋄ :EndIf 7 | 8 | v1←⍳3 9 | v1+←1 10 | vtv←'This' 'is' 'Dyalog' 'APL' 11 | :If 2 3 4 Check v1 12 | →0 Because'1+⍳3 was found to not match 2 3 4!!!' ⋄ :EndIf 13 | 14 | :If ~32767 Check 32767 Because'updating variable "r"' 15 | :If 0=''⍴⍴r ⋄ r←'fn "Because" did not update global "r"' ⋄ →0 ⋄ :EndIf 16 | r←'' 17 | :Else 18 | r←'fn "Because" did not return its ⍺' ⋄ →0 19 | :EndIf 20 | 21 | :If 3 IsNotElement v1 22 | →0 Because'"IsNotElement" reported incorrect result' ⋄ :EndIf 23 | 24 | :If 'APL'IsNotElement vtv 25 | →0 Because'"IsNotElement" failed on vtv' ⋄ :EndIf 26 | halt∆←##.halt ⋄ ##.halt←0 ⍝ disable halt because the following is expected to fail! 27 | :If ~0 IsNotElement v1 28 | →0 Because'"IsNotElement" reported incorrect result' ⋄ :EndIf 29 | ##.halt←halt∆ ⍝ reset it 30 | -------------------------------------------------------------------------------- /Tests/CompCheck/README.md: -------------------------------------------------------------------------------- 1 | # CompCheck 2 | 3 | A compatibility-checker for [Dyalog APL](http://dyalog.com). 4 | 5 | Helps to find out if Dyalog-code will run under a certain (older) version (12.1 or later) and/or in a Classic interpreter. 6 | 7 | ## Usage 8 | 9 | * Clone the repository (or download to a folder of your choice) 10 | 11 | * ]link.create /git/CompCheck #.CompCheck ⍝ adjust names as you like 12 | 13 | * `15 #.CompCheck.Run #.foo` 14 | 15 | test function (or ns) foo for compatibility with version ≥ v15 16 | 17 | * `1 #.CompCheck '/git/goo/hoo.dyalog` 18 | 19 | test file `hoo.dyalog` for compatibility with Classic interpreter 20 | 21 | * `14.1 1 #.CompCheck '/git/goo/hoo.dyalog` 22 | 23 | test file `hoo.dyalog` for compatibility with Classic interpreter and versions ≥14.1 24 | 25 | A result of `1` indicates success, 0 indicates lack of compatibility. Session-output will explain highlight incompatibilities and provide a summary with offending symbols that were found. 26 | 27 | ## Contributing 28 | 29 | You are invited to contribute to this repository through pull-requests or by creating issues. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Dyalog Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Tests/test_nousource.aplf: -------------------------------------------------------------------------------- 1 | r←test_nosource;n;sw;z;ch;bl;log 2 | r←'' 3 | :If 19≤##.DyaVersion 4 | :For sw :In 0 1 '' ⍝ testing switch values 5 | :For n :In 1 0 ⍝ and values for nosource as TARGET parameter 6 | log←##.Build txt←##.TESTSOURCE,'nosource/test_nosource',(⍕n),'.dyalogbuild -q -c',((sw≢'')∧sw≢n)/' -nosource=',⍕sw 7 | :If ''≡log 8 | ~0 Because'non-empty log after building "',txt,'"' ⋄ :EndIf 9 | 10 | z←(sw≡1)∨(sw≢0)∧n=1 ⍝ z indicates expected effect of "nosource" 11 | :For ch :In 'fnWithBlanks' 'SourceHasBlanks' ⍝ functions to analyze 12 | bl←+/∊' '⍷∊62 #.⎕ATX ch 13 | :EndFor 14 | :If z ⍝ if blanks should have been remoced 15 | :If 0 Check 0 ')∘,¨res,¨⊂##.NL ⋄ :EndIf 29 | 30 | logfile←##.TESTSOURCE,'DBuildWSFULL' 31 | ret←300 sub_RunAPLProcess(##.TESTSOURCE,'lx')('RunUCMD="DEVOPS.DBuild ',##.TESTSOURCE,'DBuild_WSFULL.dyalogbuild -q" CITA_LOG="',logfile,'" CITAnqOFF=1') 32 | 33 | :If 1 Check ⎕NEXISTS f←##.TESTSOURCE,'RunUCMD.log' ⍝ was a log-file written? 34 | →0 Because'Failing build not produce log-file "',f,'"' ⋄ :EndIf 35 | 36 | t←1⊃⎕NGET f 37 | :If 1 Check 0<≢('big.←'⎕S'&')t 38 | →0 Because'Log-file "',f,'" does not seem to contain error msgs related to ← assignment of "big" variables' ⋄ :EndIf 39 | 40 | 1(⎕NDELETE ⎕OPT'Wildcard' 1)logfile,'.*' ⍝ can do because LogFile has no "_", so we won't delete production files 41 | 1 ⎕NDELETE ##.TESTSOURCE,'testWSFULL.log' 42 | 1 ⎕NDELETE ##.TESTSOURCE,'RunUCMD.log' 43 | 1 ⎕NDELETE ##.TESTSOURCE,'testWSFULL.log.json' 44 | 1 ⎕NDELETE ##.TESTSOURCE,'MemRep.dcf' 45 | -------------------------------------------------------------------------------- /Tests/test_DBuild_tube.dyalog: -------------------------------------------------------------------------------- 1 | r←test_DBuild_tube sink;diff;compare;xp;bd;cnt;dir;t;rc;cfgFile 2 | ⍝ tests DBuild by building the tube ws and comparing it to the shipped version 3 | r←'' 4 | ⎕TRAP←0 'S' 5 | 6 | 7 | rc←(0.001×⌊/⍬)sub_RunAPLProcess(##.TESTSOURCE,'lx.dws')('RunUCMD="DBuild /git/dyalog-tube-ws/dyalog-tube-ws.dyalogbuild -quiet -prod -off -save=1" CITA_LOG=tube.log') 8 | 9 | ⍝ 'Depth'⎕CY'dfns' ⍝ is missing in 190c32. This (updated) version taken from https://github.com/abrudz/primitives/blob/main/depth.aplo 10 | Depth←{ ⍝ ⍥ 11 | 0::⎕SIGNAL⊂⎕DMX.(('EN'EN)('EM'EM)('Message'(OSError{⍵,2⌽(×≢⊃⍬⍴2⌽⍺,⊂'')/'") ("',⊃⍬⍴2⌽⍺}Message))) 12 | ncs←⎕NC↑'⍺' '⍵⍵' 13 | 0 3≡ncs:⍺⍺ ⍵⍵ ⍵ ⍝ f⍥g Y 14 | 2 3≡ncs:⍺ ⍺⍺⍥⍵⍵ ⍵ ⍝ X f⍥g Y 15 | 16 | 1<≢⍴⍵⍵:⎕SIGNAL 4 ⍝ non-vec/scal: RANK 17 | 1≠1 4⍸≢⍵⍵:⎕SIGNAL 5 ⍝ not 1...3 elements: LENGTH 18 | (c←⎕NS ⍬).⎕CT←1E¯14 ⍝ tolerant space 19 | c.≢∘⌊⍨⍵⍵:⎕SIGNAL 11 ⍝ not ints: DOMAIN 20 | 21 | 0∊⎕NC'⍺':0⊢∘⍺⍺∇∇⍵⍵⊢⍵ ⍝ monadic: placeholder left arg 22 | 23 | ⍺←{⍵ ⋄ ⍺⍺} ⍝ monadic: pass-thorugh 24 | k←⌽3⍴⌽⍵⍵ ⍝ r → r r r q r → r q r p q r → p q r 25 | n←k c.<0 26 | d←|≡¨3⍴⍵ ⍺ ⍵ ⍵ 27 | (n/k)+←n/d 28 | k⌊←d 29 | 30 | b←1↓k/b:S∘⍵¨⍺ 39 | c.∧/b:⍺ S¨⍵ 40 | } 41 | 42 | 'xp'⎕NS'' ⍝ what we expect to see 43 | xp.⎕CY'tube' ⍝ is the original version that is shipped with the product 44 | 45 | 'bd'⎕NS'' 46 | bd.⎕CY (2⎕nq'.' 'GetEnvironment' 'GitPath'),'/dyalog-tube-ws/tube' ⍝ compare to the ws that was just build 47 | 48 | compare←{ 49 | 0=≢⍵:'' 50 | n1←(1⊃⍺).⎕NC ⍵ 51 | n1=9:(⊂⍺⍎¨⊂⍵)∇¨↓(1⊃⍺).(⍎⍵).⎕NL 2 3 9 52 | n1=3:(≢/¯3↓¨⍺.⎕NR⊂⍵)/'⎕VR of functions does not match' ⍝ remove the last 3 lines of NR to get rid of SALT Info... 53 | 0::'Error comparing ',(1⊃⍺).⍵,': ',⎕JSON ⎕DMX 54 | (n1>0)∧n1≠(2⊃⍺).⎕NC ⍵:'nameclass of ',(⍕1⊃⍺),'.',⍵,' did not match with obj in new ws (nc=',(⍕(2⊃⍺).⎕NC ⍵),')' 55 | (⍴(1⊃⍺)⍎⍵)≢⍴(2⊃⍺)⍎⍵:'shape of ',(⍕1⊃⍺),'.',⍵,' did not match with obj in new ws' 56 | ((1⊃⍺)⍎⍵)≢(2⊃⍺)⍎⍵:'content of ',(⍕1⊃⍺),'.',⍵,' did not match with obj in new ws' 57 | '' 58 | } 59 | 60 | diff←2 3 9{ 61 | (⊂⍵)compare¨↓(1⊃⍵).⎕NL ⍺ 62 | }¨⊂xp bd 63 | 64 | :If 0<≢' '~⍨∊diff 65 | r←∊({0<≢⍵:⍵,⎕UCS 13 ⋄ ''}Depth 1)diff 66 | :EndIf 67 | -------------------------------------------------------------------------------- /Tests/test_DBuild_3.dyalog: -------------------------------------------------------------------------------- 1 | r←test_DBuild_3 dummy;l 2 | r←'' 3 | :If 17.1≤##.DyaVersion ⍝ this test requires v17.1 or better 4 | sort←{⍵[⍋⍵]} 5 | ⍝ create some fns/vars to test if "-c" really clears them... 6 | #.⎕CY'dfns' 7 | #.⎕ex'foo' 8 | #.foo←'goo' 9 | 10 | ⍝ run build-script (non-prod mode) 11 | sink←##.Build ##.TESTSOURCE,'DBuild_3.dyalogbuild -c -q=2' 12 | 13 | :If (sort'MyNS0' 'MyNS1' 'conga' 'httpcommand')Check l←sort #.⎕NL-9 14 | →0 Because'Did not find exactly four namespace in # but got ',⍕l ⋄ :EndIf 15 | :If 0 0 Check #.MyNS0.(⎕IO ⎕ML) 16 | →0 Because'New namespace MyNS0 did not have expected ⎕IO/⎕ML (according to defaults) set in script' ⋄ :EndIf 17 | :If 1 1 Check #.MyNS1.(⎕IO ⎕ML) 18 | →0 Because'New namespace MyNS1 did not have expected ⎕IO/⎕ML (according to defaults) set in script' ⋄ :EndIf 19 | 20 | :If 'Dollar' 'MyEnvVar' 'ProdFlag'Check #.⎕NL-2.1 21 | →0 Because'Did not find exactly 3 variables in #' ⋄ :EndIf 22 | 23 | :If (0/⊂'')Check #.⎕NL-3.1 24 | →0 Because'List of functions in # not empty as expected!' ⋄ :EndIf 25 | 26 | :If 'Test'Check #.ProdFlag 27 | →0 Because'ProdFlag did not have expected value "Test", but rather "',#.ProdFlag,'"' ⋄ :EndIf 28 | 29 | :If 2=⎕NC'dbval' 30 | :If 0×dbval Check #.MyEnvVar 31 | →0 Because'EnvironmentVariable was not retrieved with correct value' ⋄ :EndIf 32 | :EndIf 33 | 34 | 'ic'#.⎕NS'' ⋄ ⎕SE.SALT.Load'InitConga -target=#.ic' 35 | 36 | :If (↑##.dtb¨↓¯1↓#.ic.⎕CR'InitConga')Check #.conga.⎕CR'InitConga' 37 | →0 Because'InitConga not loaded identically to ]LOAD' ⋄ :EndIf 38 | 39 | :If (,⊂'HttpCommand')Check #.httpcommand.⎕NL ¯9 40 | →0 Because'HttpCommand was not loaded into httpcommand' 41 | 42 | :If 2 Check #.⎕ML 43 | →0 Because'DEFAULTS did not correctly process ⎕ML' ⋄ :EndIf 44 | :If 0 Check #.⎕IO 45 | →0 Because'DEFAULTS did not correctly process ⎕IO' ⋄ :EndIf 46 | :If 1E¯11 Check #.⎕CT 47 | →0 Because'DEFAULTS did not correctly process ⎕CT' ⋄ :EndIf 48 | :If 11 Check #.⎕PP 49 | →0 Because'DEFAULTS did not correctly process ⎕PP' ⋄ :EndIf 50 | 51 | ⍝ re-run build-script (this time in production mode) 52 | sink←##.Build ##.TESTSOURCE,'DBuild_3.dyalogbuild -c -p -q=2' 53 | 54 | :If 'Production'Check #.ProdFlag 55 | →0 Because'ProdFlag did not have expected value "Production", but rather "',#.ProdFlag,'"' ⋄ :EndIf 56 | :EndIf 57 | :EndIf 58 | -------------------------------------------------------------------------------- /Tests/test_DBuild_1.dyalog: -------------------------------------------------------------------------------- 1 | r←test_DBuild_1 dummy;pwd;cmd;args 2 | r←'' 3 | ⍝ put a few things into # to be sure that "-c" clear them! 4 | #.⎕CY'dfns' 5 | #.⎕EX'foo' ⋄ #.foo←'goo' 6 | 7 | ⍝ run build-script (non-prod mode) 8 | res←##.Build args←##.TESTSOURCE,'DBuild_1.dyalogbuild -clear -quiet=2 -save=0' 9 | 10 | :If 0 Check∨/' errors encountered.'⍷∊res 11 | →0 Because('DBuild did not succeed but reported: ',res),(⎕UCS 13),']DBuild ',args,' ⍝ to execute it...' ⋄ :EndIf 12 | 13 | :If 'MyNS0' 'MyNS1'Check #.⎕NL ¯9 14 | →0 Because'Did not find exactly two namespaces in # but instead got: ',⍕#.⎕NL ¯9 ⋄ :EndIf 15 | 16 | :If 0 0 Check #.MyNS0.(⎕IO ⎕ML) 17 | →0 Because'New namespace MyNS0 did not have expected ⎕IO/⎕ML (according to defaults) set in script' ⋄ :EndIf 18 | :If 1 1 Check #.MyNS1.(⎕IO ⎕ML) 19 | →0 Because'New namespace MyNS1 did not have expected ⎕IO/⎕ML (according to defaults) set in script' ⋄ :EndIf 20 | 21 | :If 'Dollar' 'MyEnvVar' 'ProdFlag'Check #.⎕NL-2.1 22 | →0 Because'Did not find exactly 3 variables in #' ⋄ :EndIf 23 | 24 | :If (0/⊂'')Check #.⎕NL-3.1 25 | →0 Because'List of functions in # not empty as expected!' ⋄ :EndIf 26 | 27 | :If 'Test'Check #.ProdFlag 28 | →0 Because'ProdFlag did not have expected value "Test", but rather "',#.ProdFlag,'"' ⋄ :EndIf 29 | 30 | :If 2=⎕NC'dbval' 31 | :If 0×dbval Check #.MyEnvVar 32 | →0 Because'EnvironmentVariable was not retrieved with correct value' ⋄ :EndIf 33 | :EndIf 34 | 35 | :If 2 Check #.⎕ML 36 | →0 Because'DEFAULTS did not correctly process ⎕ML' ⋄ :EndIf 37 | :If 0 Check #.⎕IO 38 | →0 Because'DEFAULTS did not correctly process ⎕IO' ⋄ :EndIf 39 | :If 1E¯11 Check #.⎕CT 40 | →0 Because'DEFAULTS did not correctly process ⎕CT' ⋄ :EndIf 41 | :If 11 Check #.⎕PP 42 | →0 Because'DEFAULTS did not correctly process ⎕PP' ⋄ :EndIf 43 | 44 | 1 Assert ×≢50#.⎕atx'MyNS0.MyNS0' ⍝ we've build w/o "-p" - do we have linked source? 45 | 46 | ⍝ re-run build-script (this time in production mode) 47 | {}##.Build,##.TESTSOURCE,'DBuild_1.dyalogbuild -clear -production -quiet=2' 48 | 49 | :If 'Production'Check #.ProdFlag 50 | →0 Because'ProdFlag did not have expected value "Production", but rather "',#.ProdFlag,'"' ⋄ :EndIf 51 | 52 | 0 Assert ≢50#.⎕atx'MyNS0.MyNS0' ⍝ check if "-production" modifier has been effective and avoided linking of source file 53 | 54 | res←##.Build args←##.TESTSOURCE,'DBuild_nameclash.dyalogbuild -clear -quiet=2 -save=0' 55 | :If 1 Check∨/'6 errors encountered.'⍷∊res 56 | →0 Because'DBuild did not fail with 4 errors (3 nameclashes + final msg) while executing...',(⎕UCS 13),']DBuild ',args ⋄ :EndIf 57 | -------------------------------------------------------------------------------- /Tests/CompCheck/Run.aplf: -------------------------------------------------------------------------------- 1 | R←{opts}Run ref;s;z;minVers;critical;c;l;sf;V;f;features;v;v∆;NL;i;Features∆;nq;uncmnt;avuClassic 2 | ⍝ opts: select tests, 3 | ⍝ 1: will it run on Classic? 4 | ⍝ and/or: minimum-version for it to run: 5 | ⍝ 12.1, 13, 13.1, 14. 14.1, 15, 16, 17, 18 6 | ⍝ ref points to a namespace or is a filename 7 | ⍝ use "⍝ CompCheck: ignore" to exclude lines with that commet 8 | NL←⎕UCS 13 9 | ⎕ML←⎕IO←R←1 ⍝ result R=1: all is well 10 | V←⍳0 ⍝ remember the versions we saw being used (and their features) 11 | 12 | features←↓1↓Features∆←Features ⍝ just the data of this table (no headers) 13 | Features∆[;2]←{('\\h\*'⎕R'')⍵}¨Features∆[;2] 14 | Features∆[;2]←{('\[\∧\\w\]'⎕R'')⍵}¨Features∆[;2] 15 | f←(≢features)⍴0 16 | :If 9=⎕NC'ref' 17 | s←⎕SRC ref 18 | :ElseIf ⎕NEXISTS ref 19 | ref←⊃2 ⎕FIX'file://',ref 20 | :Select ⌊⎕NC ref 21 | :Case,9 22 | s←⎕src⍎ref 23 | s←∊s,¨⎕ucs 10 ⍝ "flatten" it 24 | :Else 25 | ⎕←'No :Case for ⎕NC "',(⍕ref),'" = ',⍕⌊⎕nc ref 26 | :EndSelect 27 | :Else 28 | ⎕←'What''s in ref? Expecting ns or filespec' 29 | ∘∘∘ 30 | :EndIf 31 | :If 12.1∊opts 32 | avuClassic←avuClassic121 33 | :Else 34 | avuClassic←avuClassic131 35 | :EndIf 36 | :If 1∊⌊opts 37 | sf←∊s 38 | z←~(⎕UCS sf)∊avuClassic,(1.1∊opts)/9061 39 | :If ∨/z 40 | ⎕←(⍕≢∪z/sf),' Non-classic characters found in ',(⍕ref),': ',∪z/sf 41 | R←0 42 | :EndIf 43 | :EndIf 44 | 45 | :If 011)/opts),0 46 | s←('∧.*⍝ CompCheck: ignore.*$'⎕R'')s 47 | s←{1↓¨(⍵=⎕UCS 10)⊂⍵}(⎕UCS 10),s ⍝ nested lines 48 | nq←{0=≠\⍵=''''}¨s ⍝ not quoted (leaves one quote, but that is no problem for this purpose) 49 | uncmnt←nq{~(∨\⍺∧⍵='⍝')}¨s ⍝ not commented 50 | s←∊((nq∧uncmnt)/¨s),¨⎕UCS 10 ⍝ back into simple string 51 | 52 | :For i :In ⍸(1↓Features∆)[;1]>minVers 53 | c←i⊃features 54 | :If 0<≢l←(('(?:\W|∧)',(2⊃c),'(?:\W|$)')⎕S'%'⎕OPT'Mode' 'L')s 55 | R←0 56 | ⎕←NL,'Found uses of ',(2⊃Features∆[1+i;]),' (introduced with V',(⍕1⊃c),') in these statements:' 57 | :If 1=3⊃c 58 | ⎕←'NB: this applies to monadic syntax only, which unfortunately we can''t reliably detect!' 59 | :EndIf 60 | ⎕←↑l 61 | V,←1⊃c 62 | f[features⍳⊂c]←≢l 63 | :EndIf 64 | :EndFor 65 | :EndIf 66 | 67 | 68 | :If 0<≢V 69 | v∆←∪1↓Features∆[;1] 70 | ⎕←NL,'*** Features∆ of later versions found in ',(⍕ref),':' 71 | :For v :In v∆∩V 72 | ⎕←(,'F4.1,< : >'⎕FMT v),((f>0)∧(1↓Features∆[;1])=v)/1↓Features∆[;2] 73 | :EndFor 74 | :EndIf -------------------------------------------------------------------------------- /Tests/test_ReturnCode.dyalog: -------------------------------------------------------------------------------- 1 | r←test_ReturnCode dummy;myapl;rc 2 | ⍝ test if DBuild ends with correct return-code! 3 | ⍝ We test this by launching another interpreter 4 | ⍝ and testing its returncode. Unfortunately atm (July 2020) 5 | ⍝ this is only possible on Windows (APLProcess.GetExitCode only usable on Win) 6 | ⍝ needs the lx-Workspace (which is used to kick off ]DTest - best way to do this across all platforms/editions... ) 7 | ⍝ that ws is borrowed from our CITA project, so its log-file is named *.CITA.log 8 | r←'' 9 | :If ##._isWin 10 | :AndIf 18≤1⊃##._Version ⍝ APLProcess was introduced with v16 11 | rc←300 sub_RunAPLProcess((##.TESTSOURCE,'lx')('CITATest=',##.TESTSOURCE,'test_retcode.dyalog mode=DTest ok=0 dtestmods="-off -loglvl=32',(##.verbose/' -verbose'),'"')) 12 | :If 21 Check rc 13 | →cleanExit Because'Failing test did not end with code 21 (returned ',(⍕rc),')' ⋄ :EndIf 14 | :If 1 Check ⎕NEXISTS ##.TESTSOURCE,'test_retcode.CITA.log' ⍝ was a log-file written? 15 | →cleanExit Because'Failing test did not produce log-file' ⋄ :EndIf 16 | :If 1 Check ⎕NEXISTS f←##.TESTSOURCE,'test_retcode.CITA.log.json' ⍝ was a log-file written? 17 | →cleanExit Because'Failing test did not produce json-file' ⋄ :EndIf 18 | :If 21 Check(⎕JSON 1⊃⎕NGET f).rc 19 | →cleanExit Because'json-file "',f,'" did not contain expected rc 21' ⋄ :EndIf 20 | 21 | 1 ⎕NDELETE ##.TESTSOURCE,'test_retcode.CITA.log' ⍝ wipe out that expected log-file 22 | 1 ⎕NDELETE ##.TESTSOURCE,'test_retcode.CITA.log.json' 23 | 24 | rc←300 sub_RunAPLProcess((##.TESTSOURCE,'lx')('CITATest=',##.TESTSOURCE,'test_retcode.dyalog mode=DTest ok=1 dtestmods="-off -loglvl=32',(##.verbose/' -verbose'),'"')) 25 | :If 20 Check rc 26 | →cleanExit Because'Succeeding test did not end with code 20 (returned ',(⍕rc),')' ⋄ :EndIf 27 | :If 0 Check ⎕NEXISTS ##.TESTSOURCE,'test_ok.log' ⍝ was a log-file written? 28 | →cleanExit Because'Succeeding test produced log-file' ⋄ :EndIf 29 | :If 1 Check ⎕NEXISTS f←##.TESTSOURCE,'test_retcode.CITA.log.json' ⍝ was a log-file written? 30 | →cleanExit Because'Succeeding test did not produce json-file' ⋄ :EndIf 31 | :If 20 Check(⎕JSON 1⊃⎕NGET f).rc 32 | →cleanExit Because'json-file "',f,'" did not contain expected rc 20' ⋄ :EndIf 33 | 1 ⎕NDELETE ##.TESTSOURCE,'test_retcode.CITA.log.json' 34 | 35 | cleanExit: 36 | 1 ⎕NDELETE ##.TESTSOURCE,'test_retcode.CITA.log' ⍝ wipe out that expected log-file 37 | 1 ⎕NDELETE ##.TESTSOURCE,'test_retcode.session.log' 38 | 1 ⎕NDELETE ##.TESTSOURCE,'test_retcode.CITA.log.json' 39 | 1 ⎕NDELETE ##.TESTSOURCE,'MemRep.dcf' ⍝ not interested in this file (forthis test) 40 | 41 | :EndIf 42 | -------------------------------------------------------------------------------- /Tests/CompCheck/Features.aplf: -------------------------------------------------------------------------------- 1 | R←Features 2 | R←1 3⍴'Version' 'Feature' 'Syntax (0=all, 1=monadic)' 3 | R⍪← 12.1 '⌶' 0 4 | R⍪← 12.1 '⍪' 1 5 | R⍪← 12.1 '⎕FCHK' 0 6 | R⍪← 12.1 '⎕XML' 0 7 | R⍪← 12.1 '2000\h*⌶' 0 8 | R⍪← 13 '2010\h*⌶' 0 9 | R⍪← 13 '2020\h*⌶' 0 10 | R⍪← 13 '4000\h*⌶' 0 11 | R⍪← 13 '4001\h*⌶' 0 12 | R⍪← 13 '4002\h*⌶' 0 13 | R⍪← 13 '4007\h*⌶' 0 14 | R⍪← 13 '⊢' 0 15 | R⍪← 13 '⊣' 0 16 | R⍪← 13 '⍠' 0 17 | R⍪← 13 '⎕DCT' 0 18 | R⍪← 13 '⎕FR' 0 19 | R⍪← 13 '⎕OPT' 0 20 | R⍪← 13 '⎕PROFILE' 0 21 | R⍪← 13 '⎕R[^\w]' 0 22 | R⍪← 13 '⎕RSI' 0 23 | R⍪← 13 '⎕S[^\w]' 0 24 | R⍪← 13.1 '⎕DMX' 0 25 | R⍪← 13.1 '⎕FHIST' 0 26 | R⍪← 14 '1159\h*⌶' 0 27 | R⍪← 14 '181\h*⌶' 0 28 | R⍪← 14 '2002\h*⌶' 0 29 | R⍪← 14 '2015\h*⌶' 0 30 | R⍪← 14 '2015\h*⌶' 0 31 | R⍪← 14 '2022\h*⌶' 0 32 | R⍪← 14 '2023\h*⌶' 0 33 | R⍪← 14 '219\h*⌶' 0 34 | R⍪← 14 '220\h*⌶' 0 35 | R⍪← 14 '2400\h*⌶' 0 36 | R⍪← 14 '2401\h*⌶' 0 37 | R⍪← 14 '8\h*⌶' 0 38 | R⍪← 14 '≢' 1 39 | R⍪← 14 '⌸' 0 40 | R⍪← 14 '⍤' 0 41 | R⍪← 14.1 '127\h*⌶' 0 42 | R⍪← 14.1 '2017\h*⌶' 0 43 | R⍪← 14.1 '2035\h*⌶' 0 44 | R⍪← 14.1 '2101\h*⌶' 0 45 | R⍪← 14.1 '2503\h*⌶' 0 46 | R⍪← 14.1 '2520\h*⌶' 0 47 | R⍪← 14.1 '3500\h*⌶' 0 48 | R⍪← 14.1 '3501\h*⌶' 0 49 | R⍪← 14.1 '3502\h*⌶' 0 50 | R⍪← 14.1 '50100\h*⌶' 0 51 | R⍪← 14.1 '7162\h*⌶' 0 52 | R⍪← 14.1 '85\h*⌶' 0 53 | R⍪← 14.1 '900\h*⌶' 0 54 | R⍪← 14.1 '950\h*⌶' 0 55 | R⍪← 14.1 '7159\h*⌶' 0 56 | R⍪← 14.1 '7160\h*⌶' 0 57 | R⍪← 15 '1500\h*⌶' 0 58 | R⍪← 15 '180\h*⌶' 0 59 | R⍪← 15 '2014\h*⌶' 0 60 | R⍪← 15 '2016\h*⌶' 0 61 | R⍪← 15 '2041\h*⌶' 0 62 | R⍪← 15 '2501\h*⌶' 0 63 | R⍪← 15 '2502\h*⌶' 0 64 | R⍪← 15 '3502\h*⌶' 0 65 | R⍪← 15 '400\h*⌶' 0 66 | R⍪← 15 '5176\h*⌶' 0 67 | R⍪← 15 '5177\h*⌶' 0 68 | R⍪← 15 '600\h*⌶' 0 69 | R⍪← 15 '819\h*⌶' 0 70 | R⍪← 15 '8415\h*⌶' 0 71 | R⍪← 15 '⎕MKDIR' 0 72 | R⍪← 15 '⎕NDELETE' 0 73 | R⍪← 15 '⎕NEXISTS' 0 74 | R⍪← 15 '⎕NGET' 0 75 | R⍪← 15 '⎕NINFO' 0 76 | R⍪← 15 '⎕NPARTS' 0 77 | R⍪← 15 '⎕NPUT' 0 78 | R⍪← 15 '⎕RL' 0 ⍝ ok, it was there before - but not in its current form (with 2=≡⍵) 79 | R⍪← 16 '2704\h*⌶' 0 80 | R⍪← 16 '@' 0 81 | R⍪← 16 '⊆' 0 82 | R⍪← 16 '⌺' 0 83 | R⍪← 16 '⍸' 0 84 | R⍪← 16 '⎕CSV' 0 85 | R⍪← 16 '⎕JSON' 0 86 | R⍪← 16 '⎕SIGNAL\h*0' 0 87 | R⍪← 16 '2704\h*⌶' 0 88 | R⍪← 17 '\n;' 0 89 | R⍪← 17 '⎕NCOPY' 0 90 | R⍪← 17 '⎕NMOVE' 0 91 | R⍪← 18 '⎕C' 0 92 | R⍪← 18 '⍤' 0 93 | R⍪← 18 '⎕U2364' 0 94 | R⍪← 18 '⍥' 0 95 | R⍪← 18 '⎕U2365' 0 96 | R⍪← 18 '⎕DT' 0 97 | R⍪← 18 '1200\h*⌶' 0 98 | -------------------------------------------------------------------------------- /Tests/test_SuccessValue.dyalog: -------------------------------------------------------------------------------- 1 | r←test_SuccessValue dummy;myapl;rc;theTest;cmdLineParams;sult 2 | ⍝ test correct handling of new optional SuccessValue in .dyalogtest files 3 | ⍝ We test this by launching another interpreter and running test that will indicate their success 4 | ⍝ as defined in the .dylogtest that is used. The result of the test is pre-determined using 5 | ⍝ param "SucVal" (which is specific to THIS test and not a generic thing) 6 | ⍝ and THE key function then is to check is the test succeeds or fails as expected (if it interprets the return value correctly) 7 | 8 | ⍝ ============= Init: set up vars a tools as needed 9 | r←'' 10 | 11 | WeHaveAlog←{⎕NEXISTS ##.TESTSOURCE,(2⊃⎕nparts theTest),'.CITA.log.json'} ⍝ was a log file written? 12 | GetJSONlog←{⍝ read the log file and return a → target in r[1] and the log in r[2] 13 | a←(∊2↑⎕NPARTS ##.TESTSOURCE),(2⊃⎕nparts theTest),'.CITA.log.json' 14 | 0::(cleanExit Because'Caught error processing ',a,':',(⎕UCS 13),(⎕JSON⎕OPT'Compact' 0)⎕DMX)⍬ 15 | res ← ⍬(⎕JSON 1⊃⎕NGET a) 16 | res ⊣ 1⎕ndelete a 17 | } 18 | 19 | ClearLogs←{⍝ delete logfiles 20 | 0<≢⍵:sink←1(⎕NDELETE ⎕OPT'Wildcard'('*'∊⍵))##.TESTSOURCE,⍵ ⍝ wipe out that expected log-file 21 | sink←1 ⎕NDELETE ##.TESTSOURCE,'test_',(2⊃⎕nparts theTest),'.CITA.log' ⍝ wipe out that expected log-file 22 | trash←1 ⎕NDELETE ##.TESTSOURCE,'test_',(2⊃⎕nparts theTest),'.CITA.log.json' 23 | ⍬ 24 | } 25 | 26 | RunTest←{⍝ execute a test (passing cmdLineParams ⍵) 27 | sink←ClearLogs ⍬ 28 | sink←##.verbose{ 29 | ⍺:⎕←⍵ 30 | ⍬ 31 | ⍝ }pars←'CITATest=',(f←##.TESTSOURCE,theTest,(~'.'∊theTest)/'.dyalogtest'),' mode=DTest ',(' dtestmods="'cmdLineParams ⍵),' -off -loglvl=32',(##.halt/' -halt'),(##.trace/' -trace'),(##.verbose/' -verbose'),'"' 32 | }pars←'CITATEST=',(f←##.TESTSOURCE,theTest,(~'.'∊theTest)/'.dyalogtest'),' mode=DTest ',(' dtestmods="'cmdLineParams ⍵),' -setup= -off -loglvl=32',(##.verbose/' -verbose'),(##.halt/' -halt'),'"' 33 | ((1+##.(halt∨trace))⊃30(0.001×⌊/⍬))sub_RunAPLProcess(##.TESTSOURCE,'lx')pars 34 | 0 35 | } 36 | 37 | 38 | 39 | 40 | Execute←{ 41 | ⍝ ⍺=expected Result: 0=success, 1=failure 42 | ⍝ ⍵= run test indicated by "theTest", passing cmdLineParams ⍵ 43 | ⍝ uses these vars: 44 | ⍝ cmdLineParams: a fn to construct the commandlineparams for the DTest call. 45 | ⍝ uses ⍺ and ⍵. ⍺ is 'dtestmods="', so you may add DTestmodifiers (by appending to ⍺) 46 | ⍝ or pass general envVars (by prefixing ⍵ to ⍺). The closing quotes for ⍺ will be added by RunTest 47 | ⍝ theTest : name of test to execute 48 | msgTheTest←theTest,' with cmdLineParams "',(''cmdLineParams ⍵),'"' 49 | sink←⍺{ 50 | ##.verbose:⎕←'Running ',msgTheTest,', expecting it to ',(1+⍺)⊃'succeed' 'fail' 51 | ⍬ 52 | }⍵ 53 | sink←RunTest ⍵ 54 | ⍝ was a log-file written? 55 | 1 Check WeHaveAlog⍕⍵:(cleanExit Because'Test ',msgTheTest,' did not produce json-file') ⍝ could also be an indication of a crash....! 56 | 57 | res←GetJSONlog⍕⍵ 58 | ⍬≢⊃res:res 59 | 60 | res←2⊃res 61 | ⍝sink←(⎕lc[1]+1)⎕stop 1⊃⎕si 62 | sink←##.verbose{⍺::⎕←'rc=',⍕ ⋄ ⍬}res.rc 63 | exp←20+⍺ 64 | exp Check res.rc:cleanExit Because(2⊃⎕SI),'[',(⍕2⊃⎕LC),'] ',((⎕IO+2⊃⎕LC)⊃⎕NR 2⊃⎕SI),' did not end with expected indicator ',(⍕exp),' but returned ',{' '=⍥⎕DR ⍵:'"',⍵,'"' ⋄ ⍕⍵}res.rc 65 | ⍬ 66 | } 67 | 68 | ⍝=========================== The tests ================================================== 69 | cmdLineParams←{'SucVal=',('b64!',⎕se._cita.base64enc'json!',1 ⎕JSON ⍵),' ',⍺,' -SuccessValue=json!0'} ⍝ pass an environment variable SucVal 70 | theTest←'test_assert.aplf' 71 | →0 Execute 0 72 | 73 | cmdLineParams←{'SucVal=',('b64!',⎕se._cita.base64enc'json!',1 ⎕JSON ⍵),' ',⍕⍺} ⍝ pass an environment variable SucVal 74 | theTest←'SuccessValueEnvVar0' 75 | →0 Execute 0 76 | →1 Execute'0' 77 | →1 Execute ,0 78 | →1 Execute 32768 79 | 80 | 81 | theTest←'SuccessValueEnvVar42' 82 | →0 Execute 42 83 | →1 Execute'json![42]' 84 | →1 Execute'OK' 85 | 86 | theTest←'SuccessValueEnvVarOK' 87 | →0 Execute'OK' 88 | →1 Execute'APL' 89 | →1 Execute'ok' 90 | →1 Execute 0 91 | 92 | theTest←'SuccessValueOK' 93 | cmdLineParams←{⍺,' -SuccessValue=',(⍕⍵),' '} ⍝ pass value as modifier for DTest command 94 | →1 Execute 0 95 | 96 | theTest←'SuccessValueLongString' 97 | cmdLineParams←{⍺,' -SuccessValue=b64!',(⍕⍵),' '} ⍝ pass value as modifier for DTest command 98 | →0 Execute ⎕se._cita.base64enc'apl!'',ZI4,<->,ZI2,<->,ZI2,< found no problems!>''⎕fmt 1 3⍴⎕TS' 99 | 100 | 101 | cleanExit: 102 | sink←ClearLogs'SuccessValue*.lo*' 103 | 1 ⎕NDELETE ##.TESTSOURCE,'MemRep.dcf' 104 | -------------------------------------------------------------------------------- /Tests/files/london1.txt: -------------------------------------------------------------------------------- 1 | //////////////////////// // London Underground // //////////////////////// District Ealing Broadway Ealing Common Acton Town Chiswick Park // Chiswick Park───┐ ∊ Turnham Green // ├──Turnham Green Gunnersbury // Gunnersbury─────┘ Kew Gardens Richmond District Turnham Green Stamford Brook Ravenscourt Park Hammersmith Barons Court West Kensington ∊ Earl's Court West Brompton Fulham Broadway Parsons Green Putney Bridge East Putney Southfields Wimbledon Park Wimbledon District Upminster Upminster Bridge Hornchurch Elm Park Dagenham East Dagenham Heathway Becontree Upney Barking East Ham Upton Park Plaistow West Ham Bromley-by-Bow Bow Road Mile End Stepney Green Whitechapel Aldgate East Tower Hill Monument Cannon Street Mansion House Blackfriars Temple Embankment Westminster St James's Park Victoria Sloane Square South Kensington Gloucester Road ∊ Earl's Court High Street Kensington Notting Hill Gate Bayswater Paddington(S) Edgware Road(S) District (Olympia) Kensington Olympia Earl's Court Piccadilly Uxbridge Hillingdon Ickenham Ruislip Ruislip Manor Eastcote Rayners Lane South Harrow Sudbury Hill Sudbury Town Alperton Park Royal North Ealing Ealing Common ∊ Acton Town South Ealing Northfields Boston Manor Osterley Hounslow East Hounslow Central Hounslow West Hatton Cross Heathrow Terminal 5 // Heathrow Terminals 1-2-3 Piccadilly // ┌─→─∘─→─┐ ↓ Heathrow Terminals 1-2-3 // │ │ Hatton Cross ↓∊Hatton Cross // ↑ ├───∘─── ↓ Heathrow Terminal 4 // │ │ Heathrow Terminals 1-2-3 // └─←─∘─←─┘ // Heathrow Terminal 4 Piccadilly Cockfosters Oakwood Southgate Arnos Grove Bounds Green Wood Green Turnpike Lane Manor House Finsbury Park Arsenal Holloway Road Caledonian Road King's Cross St Pancras Russell Square Holborn Covent Garden Leicester Square Piccadilly Circus Green Park Hyde Park Corner Knightsbridge South Kensington Gloucester Road Earl's Court Barons Court Hammersmith Turnham Green Acton Town Hammersmith & City Barking East Ham Upton Park Plaistow West Ham Bromley-by-Bow Bow Road Mile End Stepney Green Whitechapel Aldgate East Liverpool Street Moorgate Barbican Farringdon King's Cross St Pancras Euston Square Great Portland Street Baker Street Edgware Road(N) Paddington (Bishop's Rd) Royal Oak Westbourne Park Ladbroke Grove Latimer Road Shepherd's Bush Market Goldhawk Road Hammersmith Subway Bow Church Bow Road Subway Tower Gateway Tower Hill Subway Bank Monument Docklands Light Railway Beckton Gallions Reach Cyprus Beckton Park Royal Albert Prince Regent Custom House // │ Royal Victoria // ∘ All Saints Canning Town // │ East India // │ Blackwall // Westferry │ Poplar + Poplar // ───∘───┬───∘───────∘─── ∊ West Ferry // └─┐ │ Blackwall ∊ West India Quay // └─┤ + Poplar // │ All Saints // ∘ West India Quay Devons Road // │ Bow Church Pudding Mill Lane Stratford Docklands Light Railway West Ferry Limehouse Shadwell Docklands Light Railway Tower Gateway ∊ Shadwell Bank Docklands Light Railway Lewisham Elverson Road Deptford Bridge Greenwich Cutty Sark Island Gardens Mudchute Crossharbour & London Arena South Quay Heron Quays Canary Wharf West India Quay Metropolitan Amersham ∊ Chalfont & Latimer Chesham Metropolitan Chalfont & Latimer Chorleywood Rickmansworth ∊ Moor Park Croxley Watford Metropolitan Moor Park Northwood Northwood Hills Pinner North Harrow ∊ Harrow-on-the-Hill West Harrow Rayners Lane Eastcote Ruislip Manor Ruislip Ickenham Hillingdon Uxbridge Metropolitan Harrow-on-the-Hill Northwick Park Preston Road Wembley Park Finchley Road Baker Street Great Portland Street Euston Square King's Cross St Pancras Farringdon Barbican Moorgate Liverpool Street Aldgate Northern Edgware Burnt Oak Colindale Hendon Central Brent Cross Golders Green Hampstead Belsize Park Chalk Farm ∊ Camden Town Kentish Town Northern Mornington Crescent ∊ Camden Town Euston (East) Northern Camden Town Mornington Crescent Euston (West) Subway Euston (East) Euston (West) Northern Euston (West) Warren Street Goodge Street Tottenham Court Road Leicester Square Charing Cross Embankment Waterloo ∊ Kennington Elephant & Castle Borough London Bridge Bank Moorgate Old Street Angel King's Cross St Pancras Euston (East) Northern Kennington Oval Stockwell Clapham North Clapham Common Clapham South Balham Tooting Bec Tooting Broadway Colliers Wood South Wimbledon Morden Northern Mill Hill East ∊ Finchley Central West Finchley Woodside Park Totteridge & Whetstone High Barnet Northern Kentish Town Tufnell Park Archway Highgate East Finchley Finchley Central Bakerloo Elephant & Castle Lambeth North Waterloo Embankment Charing Cross Piccadilly Circus Oxford Circus Regent's Park Baker Street Marylebone Edgware Road Paddington(N) Warwick Avenue Maida Vale Kilburn Park Queen's Park Kensal Green Willesden Junction Harlesden Stonebridge Park Wembley Central North Wembley South Kenton Kenton Harrow & Wealdstone Circle Hammersmith Goldhawk Road Shepherd's Bush Market Wood Lane Latimer Road Ladbroke Grove Westbourne Park Royal Oak Paddington (Bishop's Rd) Edgware Road(N) Baker Street Great Portland Street Euston Square King's Cross St Pancras Farringdon Barbican Moorgate Liverpool Street Aldgate Tower Hill Monument Cannon Street Mansion House Blackfriars Temple Embankment Westminster St James's Park Victoria Sloane Square South Kensington Gloucester Road High Street Kensington Notting Hill Gate Bayswater Paddington(S) Edgware Road(S) Jubilee Stratford West Ham Canning Town North Greenwich Canary Wharf Canada Water Bermondsey London Bridge Southwark Waterloo Westminster Green Park Bond Street Baker Street St John's Wood Swiss Cottage Finchley Road West Hampstead Kilburn Willesden Green Dollis Hill Neasden Wembley Park Kingsbury Queensbury Canons Park Stanmore Central Ongar North Weald Epping Theydon Bois Debden Loughton Buckhurst Hill ∊ Woodford Roding Valley Chigwell Grange Hill Hainault Fairlop Barkingside Newbury Park Gants Hill Redbridge Wanstead ∊ Leytonstone Snaresbrook South Woodford Woodford Central Leytonstone Leyton Stratford Mile End Bethnal Green Liverpool Street Bank St Paul's Chancery Lane Holborn Tottenham Court Road Oxford Circus Bond Street Marble Arch Lancaster Gate Queensway Notting Hill Gate Holland Park Shepherd's Bush White City East Acton North Acton Central West Ruislip Ruislip Gardens South Ruislip Northolt Greenford Perivale Hanger Lane ∊ North Acton West Acton Ealing Broadway Waterloo & City Bank Waterloo Victoria Brixton Stockwell Vauxhall Pimlico Victoria Green Park Oxford Circus Warren Street Euston (East) King's Cross St Pancras Highbury & Islington Finsbury Park Seven Sisters Tottenham Hale Blackhorse Road Walthamstow Central East London New Cross Gate ∊ Surrey Quays New Cross East London Surrey Quays Canada Water Rotherhithe Wapping Shadwell Whitechapel Shoreditch National Rail North Woolwich Silvertown Custom House Canning Town West Ham Stratford Hackney Wick Homerton Hackney Central Dalston Kingsland Canonbury Highbury & Islington Caledonian Road & Barnsbury Camden Road Kentish Town West Gospel Oak Hampstead Heath Finchley Road & Frognal West Hampstead Brondesbury Brondesbury Park Kensal Rise Willesden Junction Acton Central South Acton Gunnersbury Kew Gardens Richmond Via Mainline Station Paddington(N) Paddington (Bishop's Rd) Via Mainline Station Paddington (Bishop's Rd) Paddington(S) Change Platform Paddington(N) Paddington(S) 2 | -------------------------------------------------------------------------------- /Tests/files/london2.ff: -------------------------------------------------------------------------------- 1 | //////////////////////// // London Underground // //////////////////////// District Ealing Broadway Ealing Common Acton Town Chiswick Park // Chiswick Park───┐ ∊ Turnham Green // ├──Turnham Green Gunnersbury // Gunnersbury─────┘ Kew Gardens Richmond District Turnham Green Stamford Brook Ravenscourt Park Hammersmith Barons Court West Kensington ∊ Earl's Court West Brompton Fulham Broadway Parsons Green Putney Bridge East Putney Southfields Wimbledon Park Wimbledon District Upminster Upminster Bridge Hornchurch Elm Park Dagenham East Dagenham Heathway Becontree Upney Barking East Ham Upton Park Plaistow West Ham Bromley-by-Bow Bow Road Mile End Stepney Green Whitechapel Aldgate East Tower Hill Monument Cannon Street Mansion House Blackfriars Temple Embankment Westminster St James's Park Victoria Sloane Square South Kensington Gloucester Road ∊ Earl's Court High Street Kensington Notting Hill Gate Bayswater Paddington(S) Edgware Road(S) District (Olympia) Kensington Olympia Earl's Court Piccadilly Uxbridge Hillingdon Ickenham Ruislip Ruislip Manor Eastcote Rayners Lane South Harrow Sudbury Hill Sudbury Town Alperton Park Royal North Ealing Ealing Common ∊ Acton Town South Ealing Northfields Boston Manor Osterley Hounslow East Hounslow Central Hounslow West Hatton Cross Heathrow Terminal 5 // Heathrow Terminals 1-2-3 Piccadilly // ┌─→─∘─→─┐ ↓ Heathrow Terminals 1-2-3 // │ │ Hatton Cross ↓∊Hatton Cross // ↑ ├───∘─── ↓ Heathrow Terminal 4 // │ │ Heathrow Terminals 1-2-3 // └─←─∘─←─┘ // Heathrow Terminal 4 Piccadilly Cockfosters Oakwood Southgate Arnos Grove Bounds Green Wood Green Turnpike Lane Manor House Finsbury Park Arsenal Holloway Road Caledonian Road King's Cross St Pancras Russell Square Holborn Covent Garden Leicester Square Piccadilly Circus Green Park Hyde Park Corner Knightsbridge South Kensington Gloucester Road Earl's Court Barons Court Hammersmith Turnham Green Acton Town Hammersmith & City Barking East Ham Upton Park Plaistow West Ham Bromley-by-Bow Bow Road Mile End Stepney Green Whitechapel Aldgate East Liverpool Street Moorgate Barbican Farringdon King's Cross St Pancras Euston Square Great Portland Street Baker Street Edgware Road(N) Paddington (Bishop's Rd) Royal Oak Westbourne Park Ladbroke Grove Latimer Road Shepherd's Bush Market Goldhawk Road Hammersmith Subway Bow Church Bow Road Subway Tower Gateway Tower Hill Subway Bank Monument Docklands Light Railway Beckton Gallions Reach Cyprus Beckton Park Royal Albert Prince Regent Custom House // │ Royal Victoria // ∘ All Saints Canning Town // │ East India // │ Blackwall // Westferry │ Poplar + Poplar // ───∘───┬───∘───────∘─── ∊ West Ferry // └─┐ │ Blackwall ∊ West India Quay // └─┤ + Poplar // │ All Saints // ∘ West India Quay Devons Road // │ Bow Church Pudding Mill Lane Stratford Docklands Light Railway West Ferry Limehouse Shadwell Docklands Light Railway Tower Gateway ∊ Shadwell Bank Docklands Light Railway Lewisham Elverson Road Deptford Bridge Greenwich Cutty Sark Island Gardens Mudchute Crossharbour & London Arena South Quay Heron Quays Canary Wharf West India Quay Metropolitan Amersham ∊ Chalfont & Latimer Chesham Metropolitan Chalfont & Latimer Chorleywood Rickmansworth ∊ Moor Park Croxley Watford Metropolitan Moor Park Northwood Northwood Hills Pinner North Harrow ∊ Harrow-on-the-Hill West Harrow Rayners Lane Eastcote Ruislip Manor Ruislip Ickenham Hillingdon Uxbridge Metropolitan Harrow-on-the-Hill Northwick Park Preston Road Wembley Park Finchley Road Baker Street Great Portland Street Euston Square King's Cross St Pancras Farringdon Barbican Moorgate Liverpool Street Aldgate Northern Edgware Burnt Oak Colindale Hendon Central Brent Cross Golders Green Hampstead Belsize Park Chalk Farm ∊ Camden Town Kentish Town Northern Mornington Crescent ∊ Camden Town Euston (East) Northern Camden Town Mornington Crescent Euston (West) Subway Euston (East) Euston (West) Northern Euston (West) Warren Street Goodge Street Tottenham Court Road Leicester Square Charing Cross Embankment Waterloo ∊ Kennington Elephant & Castle Borough London Bridge Bank Moorgate Old Street Angel King's Cross St Pancras Euston (East) Northern Kennington Oval Stockwell Clapham North Clapham Common Clapham South Balham Tooting Bec Tooting Broadway Colliers Wood South Wimbledon Morden Northern Mill Hill East ∊ Finchley Central West Finchley Woodside Park Totteridge & Whetstone High Barnet Northern Kentish Town Tufnell Park Archway Highgate East Finchley Finchley Central Bakerloo Elephant & Castle Lambeth North Waterloo Embankment Charing Cross Piccadilly Circus Oxford Circus Regent's Park Baker Street Marylebone Edgware Road Paddington(N) Warwick Avenue Maida Vale Kilburn Park Queen's Park Kensal Green Willesden Junction Harlesden Stonebridge Park Wembley Central North Wembley South Kenton Kenton Harrow & Wealdstone Circle Hammersmith Goldhawk Road Shepherd's Bush Market Wood Lane Latimer Road Ladbroke Grove Westbourne Park Royal Oak Paddington (Bishop's Rd) Edgware Road(N) Baker Street Great Portland Street Euston Square King's Cross St Pancras Farringdon Barbican Moorgate Liverpool Street Aldgate Tower Hill Monument Cannon Street Mansion House Blackfriars Temple Embankment Westminster St James's Park Victoria Sloane Square South Kensington Gloucester Road High Street Kensington Notting Hill Gate Bayswater Paddington(S) Edgware Road(S) Jubilee Stratford West Ham Canning Town North Greenwich Canary Wharf Canada Water Bermondsey London Bridge Southwark Waterloo Westminster Green Park Bond Street Baker Street St John's Wood Swiss Cottage Finchley Road West Hampstead Kilburn Willesden Green Dollis Hill Neasden Wembley Park Kingsbury Queensbury Canons Park Stanmore Central Ongar North Weald Epping Theydon Bois Debden Loughton Buckhurst Hill ∊ Woodford Roding Valley Chigwell Grange Hill Hainault Fairlop Barkingside Newbury Park Gants Hill Redbridge Wanstead ∊ Leytonstone Snaresbrook South Woodford Woodford Central Leytonstone Leyton Stratford Mile End Bethnal Green Liverpool Street Bank St Paul's Chancery Lane Holborn Tottenham Court Road Oxford Circus Bond Street Marble Arch Lancaster Gate Queensway Notting Hill Gate Holland Park Shepherd's Bush White City East Acton North Acton Central West Ruislip Ruislip Gardens South Ruislip Northolt Greenford Perivale Hanger Lane ∊ North Acton West Acton Ealing Broadway Waterloo & City Bank Waterloo Victoria Brixton Stockwell Vauxhall Pimlico Victoria Green Park Oxford Circus Warren Street Euston (East) King's Cross St Pancras Highbury & Islington Finsbury Park Seven Sisters Tottenham Hale Blackhorse Road Walthamstow Central East London New Cross Gate ∊ Surrey Quays New Cross East London Surrey Quays Canada Water Rotherhithe Wapping Shadwell Whitechapel Shoreditch National Rail North Woolwich Silvertown Custom House Canning Town West Ham Stratford Hackney Wick Homerton Hackney Central Dalston Kingsland Canonbury Highbury & Islington Caledonian Road & Barnsbury Camden Road Kentish Town West Gospel Oak Hampstead Heath Finchley Road & Frognal West Hampstead Brondesbury Brondesbury Park Kensal Rise Willesden Junction Acton Central South Acton Gunnersbury Kew Gardens Richmond Via Mainline Station Paddington(N) Paddington (Bishop's Rd) Via Mainline Station Paddington (Bishop's Rd) Paddington(S) Change Platform Paddington(N) Paddington(S) 2 | -------------------------------------------------------------------------------- /DyalogBuild.dyalog: -------------------------------------------------------------------------------- 1 | :Namespace DyalogBuild ⍝ V 1.87.3 2 | ⍝ 2017 04 11 MKrom: initial code 3 | ⍝ 2017 05 09 Adam: included in 16.0, upgrade to code standards 4 | ⍝ 2017 05 21 MKrom: lowercase Because and Check to prevent breaking exisitng code 5 | ⍝ 2017 05 22 Adam: reformat 6 | ⍝ 2017 05 23 Adam: uppercased Because and Check and moved Words, reinstate blank lines, make compatible with 15.0 7 | ⍝ 2017 06 05 Adam: typo 8 | ⍝ 2017 06 15 Adam: TOOLS → EXPERIMENTAL 9 | ⍝ 2017 06 18 MKrom: test for missing argument 10 | ⍝ 2017 06 23 MKrom: get rid of empty function names 11 | ⍝ 2017 06 24 Brian: Allow commented lines in dyalogtest files, drop leading comma in args.tests, exit gracefully if no tests defined 12 | ⍝ 2017 06 27 Adam: Add disclaimer to help 13 | ⍝ 2017 07 24 MKrom: save exists state 14 | ⍝ 2017 12 12 Brian: DBuild - allow SALT "special" locations (e.g. [DYALOG], et al), add 'apl' as loadable type 15 | ⍝ 2018 01 12 Brian: DBuild - if the user specifies a folder and there's only a single .dyalogbuild file, use it 16 | ⍝ 2018 03 28 MKrom: Add "PROD" command, as EXEC but only executed for production builds 17 | ⍝ 2018 03 28 MKrom: Tolerate tab in place of space 18 | ⍝ 2018 03 29 MKrom: Also look for .dyalogtest file below WSFOLDER and WSFOLDER/Tests 19 | ⍝ 2018 03 29 MKrom: Added "Fail" function 20 | ⍝ 2018 04 18 Adam: ]??cmd → ]cmd -?? 21 | ⍝ 2018 05 01 Adam: add SVN tag 22 | ⍝ 2018 05 07 Adam: help text tweak 23 | ⍝ 2018 05 17 MKrom: Bugfix: EXEC commands were not being executed in production builds 24 | ⍝ 2018 05 19 MKrom: Report errors in result of ]dbuild 25 | ⍝ 2018 05 24 Adam: Move to DEVOPS 26 | ⍝ 2018 06 27 Adam: Remove "experimental" warning 27 | ⍝ 2018 06 28 Adam: Check actual version. Add DATA: path, Format=charvecs(default)/charvec/charmat/json, SetEOL=crlf/etc. 28 | ⍝ 2018 09 28 Brian: DTest - Allow wildcards ? and * in test function names (regex will also work) 29 | ⍝ 2018 11 13 Adam: help tweak 30 | ⍝ 2019 02 01 Adam: help 31 | ⍝ 2019 02 13 Brian: allow commented lines in build scripts 32 | ⍝ 2019 03 14 MBaas: handle nested msgs 33 | ⍝ 2020 01 21 MBaas: mostly backward compatible with v12 (need to sort out ⎕R/⎕S,@) 34 | ⍝ 2020 01 22 MBaas: compatibility with Classic 35 | ⍝ 2020 01 23 MBaas: took care of ⎕R,⎕S,@ 36 | ⍝ 2020 01 24 MBaas: DBuild: added switch -halt for "halt on error" as in DTest; fixed bugs found while testing under v12 37 | ⍝ 2020 01 27 MBaas: DBuild: target: wsid, save=1, off=1 (and switch -save=1 to override settings from file);]DBuild: added -TestClassic 38 | ⍝ 2020 01 28 MBaas: DBuild: -TestClassic=version;bug fixes;doco 39 | ⍝ 2020 01 29 MBaas: DBuild: $EnvVar 40 | ⍝ 2020 03 23 MBaas: made TestClassic is a simple switch w/o values assigned; fixes dealing with -halt in -save in DBuild;various minor fixes 41 | ⍝ 2020 04 03 MBaas: added -clear to DTest to make sure that the ws is ⎕CLEARed before executing tests (simplifies repeated testing) 42 | ⍝ 2020 04 06 MBaas: ]DBuild 1.25 executes the content of secret variable ⎕SE.DBuild_postSave after saving the ws 43 | ⍝ 2020 04 15 MBaas: ]DTest {file} now loads ALL functions present in folder of {file}, but only execute the test specified in file. (So test may use utils w/o bothering about loading them) 44 | ⍝ 2020 04 21 MBaas: ]DTest - timestamp (adds ⎕TS to log messages) 45 | ⍝ 2020 04 23 MBaas: ]DTest: renamed -timestamp to -ts; added -timeout 46 | ⍝ 2020 04 29 MBaas: ]DTest -order=0|1|"NumVec". Default is random order when executing tests and setups. If tests fail, order will be accessible in *.rng.txt files! 47 | ⍝ 2020 05 19 MBaas: colon in arguments of the instructions (i.e. LX/EXEC/PROD/DEFAULTS with pathnames) caused trouble. Fixed. 48 | ⍝ 2020 05 20 MBaas: variables with platform info; typos fixed (in Help);]DBuild only creates logfile (with -off) if it found errors 49 | ⍝ 2020 07 01 MBaas: ]DTest -init; fixed bugs when ]DBuild used -save Modifier 50 | ⍝ 2020 07 23 MBaas: v1.30 ]DTest -off;lots of small fixes & enhancements - see commit message for details 51 | ⍝ 2020 07 24 MBaas: some fixes for compatibility with old Dyalog versions 52 | ⍝ 2020 08 05 AWS: Avoid calling ⎕USING if on AIX or using a classic interpreter - avoids extraneous errors on status window stream 53 | ⍝ 2020 08 21 MBaas: v1.31 ]DTest accepts any # of arguments (can be useful for selection of Tests as in DUI QAs) 54 | ⍝ 2021 02 04 MBaas: v1.4 ]DBuild deals with ]LINKed files;fixed various bugs in Build & Test 55 | ⍝ 2021 01 10 Adam: v1.32 defer getting .NET Version until needed 56 | ⍝ 2021 01 20 MBaas: v1.33 moved assignments into dedicated Init function to avoid running them when UCMD is loaded 57 | ⍝ 2021 03 16 MBaas: v1.41 merging of various minor changes, mostly TACIT related 58 | ⍝ 2021 03 23 MBaas: v1.43 fixes: Classic compatibility, relative paths for tests & suites 59 | ⍝ 2021 03 30 MBaas: v1.44 fixes: more Classic compatibility - missed a few things with 1.43, but now it should be done. 60 | ⍝ 2021 04 22 MBaas: v1.45: improved loading of code (from .dyalog + .apln,.aplc,.aplf,.apli,.aplo);various fixes & cleanups 61 | ⍝ 2021 04 30 MBaas: v1.46 has a better workaround for saving (no need to go into the session);-save=0 can overwrite the option set with TARGET 62 | ⍝ 2021 05 19 MBaas: v1.50 special handling of WS FULL in DTest and DBuild; allows specifying TARGET with .exe or .dll extension;handle multiple TARGET entries per file 63 | ⍝ 2021 06 02 MBaas: v1.51: v1.50 did not report ALL errors 64 | ⍝ 2021 06 24 MBaas: v1.52: minor details 65 | ⍝ 2021 07 26 MBaas: v1.53: fixed VALUE ERROR in ]DBuild 66 | ⍝ 2021 07 29 MBaas: v1.60: added switch to -coco to ]DTest to enable testing of Code Coverage (requires version ≥18.0) 67 | ⍝ 2021 09 01 MBaas: v1.61: DTest -off=2 to create .log file for failed tests, but not ⎕OFF (useful when called by other functions, as CITA does) 68 | ⍝ -testlog also create {basename}.session.log ALWAYS (success or failure) with the entire session output of executing the test, launching a test shows current version of DyalogBuild 69 | ⍝ DTest: -trace switch will also trace into setup functions, not only tests. 70 | ⍝ Help for DBuild or DTest will also show version number 71 | ⍝ 2021 12 17 MBaas, v1.62: new internal variable (available to tests): _isCITA - is set to 1 if running under control of CITA (Continous Integration Tool for APL) 72 | ⍝ v1.62: streamlined logging and creation of logfiles (reporting errors and optionally info and warnings, too) 73 | ⍝ v1.62: it is also possible to get test results in a .json file (see loglvl): this file also has performance stats and collects various memory related data 74 | ⍝ 2022 01 10 MBaas, v1.63: DBuild: ⎕WSID will not be set if save=0 (use save=2 to not save, but set ⎕WSID). "-q" modifier suppresses ALL logging (only logs errors) 75 | ⍝ 2022 05 26 MBaas, v1.70: DBuild: DATA: support for .TXT files was mistakenly removed with 1.4 - fixed that. Also: DEFAULTS were never applied to # - now they are. New modifier -target to override TARGET. 76 | ⍝ also incompatible change: to import *.APLA use the APL directive! DATA used to support this, but it now strictly reads file content and assigns it. 77 | ⍝ Removed code for compatibility with old versions. DBuild/DTest 1.7 requires at least Dyalog v18.0. 78 | ⍝ Various little tweaks in DBuild & DTest and its tools (for example, if -halt is used, Check will produce more verbose output & explanation). 79 | ⍝ Renamed switch "-coco" to "-coverage" 80 | ⍝ Support for "SuccessValue" in .dyalogtest file: in case tests would return boolean result instead of string. (see help for details) 81 | ⍝ SuccessValue defines the exact value which test return to indicate "success". Anything else will be interpreted as sign of failure. 82 | ⍝ The values for the setup and teardown modifiers are now optional, so you can avoid running any by using the modifier w/o value (so -setup will run NO setups) 83 | ⍝ Added Assert for "lighter" tests (details: https://github.com/Dyalog/DBuildTest/wiki/Assert) 84 | ⍝ 2022 07 01 MBaas, v1.71: made Log less verbose when msg type is provided;PerfStats now contain "raw" (unformatted) data which makes it easier to analyse 85 | ⍝ 2022 07 25 MBaas, v1.72: fixed issues with the workarounds of "0⎕SAVE problem" (refs from ⎕SE to #) 86 | ⍝ 2022 07 26 MBaas, v1.73: DBuild: dealt with error if wsid contains invalid path; minor addition to help for -prod flag 87 | ⍝ 2022 08 15 MBaas, v1.74: DBuild: addressed #11 (if prod is set, -quiet will default to 1 and -save to 0); shorter log for loading of files to avoid linebreaks;added check for valence of setup/teardown/test functions 88 | ⍝ 2022 08 26 MBaas, v1.75: DBuild & DTest: tweaked help texts. DBuild: the mechanism to use config parameters is more robust and supports alternate notations. 89 | ⍝ 2022 09 16 MBaas, v1.76: DTest: rearrenged code setup of "ns" for .dyalogest files (##.verbose; setup/test/teardown fns can also ne niladic now; result of setup was not tested against SuccessValue; fixed handling of CodeCoverage_Subject in test suites. 90 | ⍝ 2022 10 07 MBaas: v1.77: DTest: CodeCoverage tweaks 91 | ⍝ 2023 02 01 MBaas, v1.78: Assert: fixed bug if a failing test is not surrounded by documenting code; result is optional now; error logs also include details of .NET Exceptions 92 | ⍝ 2023 02 03 MBaas, v1.79: Assert also shows ⍺ and ⍵ (if their tally is 60 or below) 93 | ⍝ 2023 02 04 MBaas, v1.80: _cita._LogStatus ensures its ⍵ is scalar when it is numeric 94 | ⍝ 2023 04 28 MBaas, v1.81: Check: additional context info in msgs of failing checks - if -halt is set, we also display the calling line 95 | ⍝ 2023 05 10 MBaas, v1.82: DTest now counts and reports the number of "Checks" that were executed (calls of function Check) 96 | ⍝ 2023 05 20 MBaas, v1.83: DBuild: the icon-parameter of the target-directive may use "./" to indicate path relative to the location of the .dyalogbuild file 97 | ⍝ 2023 07 05 MBaas, v1.84: DBuild: added "nousource" directive and option for TARGET to save a dws w/o source (neccessary when building for Classic & Unicode!) 98 | ⍝ 2023 09 25 MBaas, v1.85: DBuild: now supports building StandaloneNativeExe on macOS and Linux as well (from v19.0 onwards). ]DTest -f= supports wildcards * and ?;DTest:RandomVal early exit if ⍵=1; ]DTest with folder argument executes single setup_ fn (previously ignored it) 99 | ⍝ 2032 09 27 MBaas, v1.85.1: DTest: fixed bug where using the "-order" with a numeric vector enclosed in " had no effect. 100 | ⍝ 2032 09 28 MBaas, v1.85.2: DTest: Version returns a "proper" numeric version no., "SemVer" has a semantic version no. 101 | ⍝ 2032 10 02 MBaas, v1.85.3: DTest: on failing tests (or setups) DTest reported SuccessValue as vec when it was scalar 102 | ⍝ 2023 10 03 MBaas, v1.85.4: "]cmd -?" shows fully qualified name of UCMD in options for more help 103 | ⍝ 2023 11 28 MBaas, v1.85.5: localized vars in SemVer 104 | ⍝ 2023 12 13 MBaas, v1.85.6: DyalogBuild accepts parameters enclosed in quotes; DBuild "-p" and "-nosource" weren't working as intended with v19 105 | ⍝ 2024 02 14 MBaas, v1.86.0: DTest: revised mechanism to load added CodeCoverage: either from a folder in one of 106 | ⍝ the UCMD folders or using CODECOVERAGE_PATH;LogError & Log were spitting out their arguments 107 | ⍝ into the session (unless q=0, but sometimes even then) - disabled that as the log should now 108 | ⍝ collect all output anyway. Pls. let us know if this a problem for you. 109 | ⍝ 2024 06 07 MBaas, v1.87.0: DTest.Check - Check was supposed to display the line of code that called check - this wasn't working and now does. 110 | ⍝ Kai has upgraded CodeCoverage to 0.10.6 which now can also process code in #. 111 | ⍝ NB: Kai's class is no longer distributed with DTest, but can be downloaded from https://github.com/aplteam/CodeCoverage/releases 112 | ⍝ Please also see prev. comment wrt location of the CodeCoverage class. (ofc we're using the latest 0.10.7) 113 | ⍝ DTest -? did not mention the -coverag flag. Fixed. 114 | ⍝ DTest: Missing setup/teardown-fns did not ⎕STOP when -halt was set 115 | ⍝ DTest: it is possible to add an empty "setup:" declaration to .dyalogtest files to avoid running any setup functions 116 | ⍝ DTest: logging was in some cases duplicated into the session - I think this is avoided now w/o any loss of information. 117 | ⍝ DTest: -clear switch also removes existing LINKs in # 118 | ⍝ 2024 08 13 MBaas, v1.87.1: DTest: Check shows more info about data type and shape of arguments when they are not equal 119 | ⍝ 2025 08 13 MBaas, v1.87.2: DTest: fixed an error when argument pointed to a folder with no tests 120 | ⍝ 2025 08 18 MBaas, v1.87.3: DTest: fixed a problem with the UCMD Help 121 | 122 | 123 | CodeCoverageVersion←'0.10.7' 124 | SuccessValue←'' 125 | ⍝ does not get in as a var with v19s startup 126 | 127 | ∇ R←DEBUG 128 | R←⎕SE.SALTUtils.DEBUG ⍝ used for testing to disable error traps ⍝ BTW, m19091 for that being "⎕se" (instead of ⎕SE) even after Edit > Reformat. 129 | ∇ 130 | :Section Compatibility 131 | ⎕IO←1 132 | ⎕ML←1 133 | 134 | ∇ R←GetDOTNETVersion;vers;⎕IO;⎕USING 135 | ⍝ R[1] = 0/1/2: 0=nothing, 1=.net Framework, 2=NET CORE 136 | ⍝ R[2] = Version (text vector) 137 | ⍝ R[3] = Version (identifiable x.y within [2] in numerical form) 138 | ⍝ R[4] = Textual description of the framework 139 | ⎕IO←1 140 | R←0 '' 0 '' 141 | :If (82=⎕DR' ')∨'AIX'≡3↑⊃'.'⎕WG'APLVersion' 142 | ⍝ calls on ⎕USING generate output which is not wanted on AIX or classic interpreters. On Windows or Unicode 143 | ⍝ .NET Core may not be installed, so the output is valid. ⎕USING may generate trappable errors in future 144 | ⍝ rendering this redundant. 145 | →0 146 | :EndIf 147 | :Trap 0 148 | ⎕USING←'System' '' 149 | vers←System.Environment.Version 150 | R[2]←⊂⍕vers 151 | R[3]←vers.(Major+0.1×Minor) 152 | :If 4=⌊R[3] ⍝ a 4 indicates .net Framework! 153 | R[1]←1 154 | :If (⍕vers)≡'4.0.30319.42000' ⍝ .NET 4.6 and higher! 155 | R[4]←⊂Runtime.InteropServices.RuntimeInformation.FrameworkDescription 156 | :ElseIf (10↑⍕vers)≡'4.0.30319.' ⍝ .NET 4, 4.5, 4.5.1, 4.5.2 157 | R[4]←⊂'.NET Framework ',2⊃R 158 | :EndIf 159 | :ElseIf 3.1=R[3] ⍝ .NET CORE 160 | :OrIf 40T' 223 | :If 0≠1⊃r←GCD 256 256 224 | r←2⊃r 225 | :Else 226 | ⎕NA'I4 kernel32.C32|GetLastError' 227 | 11 ⎕SIGNAL⍨'GetCurrentDirectory error:',⍕GetLastError 228 | :EndIf 229 | :EndSelect 230 | ∇ 231 | 232 | ∇ r←{quietly}_SH cmd 233 | :Access public shared 234 | quietly←{6::⍵ ⋄ quietly}0 235 | :If quietly 236 | cmd←cmd,' &1' 237 | :EndIf 238 | r←{0::'' ⋄ ⎕SH ⍵}cmd 239 | ∇ 240 | 241 | 242 | ∇ r←{pattern}ListPost15 path 243 | :If 0=⎕NC'pattern' 244 | :OrIf pattern≡'' 245 | pattern←'' 246 | :Else 247 | path,←(~path[≢path]∊'\/')/'/' 248 | :EndIf 249 | r←⍉↑0 2 9 1(⎕NINFO ⎕OPT 1)(path,pattern) ⍝ CompCheck: ignore 250 | r[;4]←r[;4]=1 251 | ∇ 252 | 253 | 254 | ∇ rslt←_Filetime_to_TS filetime;⎕IO 255 | :If 1≠0⊃rslt←FileTimeToLocalFileTime filetime(⎕IO←0) 256 | :OrIf 1≠0⊃rslt←FileTimeToSystemTime(1⊃rslt)0 257 | rslt←0 0 ⍝ if either call failed then zero the time elements 258 | :EndIf 259 | rslt←1 1 0 1 1 1 1 1/1⊃rslt ⍝ remove day of week 260 | ∇ 261 | 262 | ∇ Chars←GetText name;nid;signature;nums;sz;b 263 | ⍝ Read ANSI or Unicode character file 264 | sz←⎕NSIZE nid←(unixfix name)⎕NTIE 0 265 | signature←⎕NREAD nid 83 3 0 266 | :If signature≡¯17 ¯69 ¯65 ⍝ UTF-8? 267 | nums←⎕NREAD nid 83 sz 268 | Chars←'UTF-8'⎕UCS 256|nums ⍝ Signed ints 269 | :ElseIf ∨/b←(2↑signature)∧.=2 2⍴¯1 ¯2 ¯2 ⍝ Unicode (UTF-16) 270 | Chars←{,⌽(2,⍨2÷⍨⍴⍵)⍴⍵}⍣(ltack/b)⎕NREAD nid 83 sz 2 271 | Chars←'UTF-16'⎕UCS(2*16)|163 ⎕DR Chars 272 | :Else ⍝ ANSI or UTF-8 273 | Chars←{11::⎕UCS ⍵ ⋄ 'UTF-8'⎕UCS ⍵}256|⎕NREAD nid 83 sz 0 274 | :EndIf 275 | ⎕NUNTIE nid 276 | ∇ 277 | 278 | ∇ vtv←GetVTV name 279 | ⍝ Read ANSI or Unicode character file as vector of text vectors 280 | :Trap 0 281 | vtv←{1↓¨(v=n)⊂v←(n←⎕UCS 10),⍵}(GetText name)~⎕UCS 13 282 | :Else ⍝ Classic V12/13 have problems with GetText of this file (as used in dVersion), so if everything failed, this is our last resort 283 | vtv←⎕SE.UnicodeFile.ReadNestedText name 284 | :EndTrap 285 | ∇ 286 | 287 | ∇ f←unixfix f;slash;space 288 | ⍝ replaces Windows file separator \ with Unix file separator / 289 | ⍝ '\ ' is denotes an escaped space under Unix - so don't change those \ 290 | ⍝ escape any spaces that remain 291 | ⍝ this approach is mindnumbingly simple and probably dangerous 292 | ⍝ which is why we call unixfix very cautiously 293 | :If (⊂APLVersion)∊'*nix' 'Mac' 294 | ⍝ fails on the unlikely (but possible to create on Windows) '\tmp\ myspace.txt' 295 | slash←'\'=f 296 | space←' '=f 297 | ((slash>1↓space,0)/f)←'/' 298 | ((space>¯1↓0,slash)/f)←⊂'\ ' 299 | f←∊f 300 | :EndIf 301 | ∇ 302 | 303 | ∇ {names}←{options}LoadCode file_target_mode;target;file;whatWeHave;f1;f2;f3;fl;fls;sep;sf;source;res;mode;larg;ref 304 | ⍝ loads code from scriptfile (NB: file points to one existing file, no pattern etc.) 305 | ⍝ Options defines SALT options 306 | ⍝ file_target_mode: (filename ) 307 | ⍝ res: nested vector of names that were defined 308 | names←0⍴⊂'' 309 | →(0=≢file_target_mode)/0 ⍝ gracefully treatment of empty calls 310 | :If 2=≢file_target_mode ⋄ file_target_mode←file_target_mode,⊂'apl' ⋄ :EndIf 311 | (file target mode)←file_target_mode 312 | :If 0=⎕NC'options' 313 | options←'' 314 | :EndIf 315 | :If target≢'' ⍝ if we have a target 316 | :AndIf 9≠⎕NC'target' ⍝ and it's not a ref 317 | :AndIf 0=⎕NC target ⍝ and doesn't even exist yet 318 | target ⎕NS'' ⍝ THEN we create it! 319 | :EndIf 320 | options←' ',options 321 | 322 | (f1 f2 f3)←⎕NPARTS file 323 | ⍝ filenames may contain wildcards - which isn't so useable with Link.Import. 324 | ⍝ So we resolve them and work through the list, processing every file as good as we can 325 | ⍝ but List may not be the right tool to do that because it does not give us a filename with extension - so we can't recognize -.apla! 326 | ⍝ OTOH, a default DIR lister would not search the SALT libraries etc. 327 | 328 | ⍝ search the specified file in the source folder and SALT's workdir (emulate SALT.Load here) 329 | sep←'∘',(1+isWin)⊃':' ';' ⍝ separator for those paths... 330 | 331 | :For sf :In (⊂f1),sep Split ⎕SE.SALT.Settings'workdir' 332 | :If ~(⊃⌽sf)∊'\/' 333 | sf,←⎕SE.SALT.FS 334 | :EndIf 335 | :If 0<⍬⍴⍴fls←(ListFiles sf,f2,f3)[;1] 336 | :For fl :In fls 337 | :If 'data'≡lc mode 338 | res←1⊃⎕NGET fl 1 339 | names,←⊂fl res 340 | :Else ⍝ mode≡pl 341 | :Select lc 3⊃⎕NPARTS fl 342 | :CaseList '.dyalog' '.aplc' '.aplf' '.apln' '.aplo' '.apli' 343 | :Trap DEBUG↓0 ⍝↓↓↓↓ be sure to pass target as a ref, bad things may happen otherwise () 344 | :If {(1=≢⍵)∧⊃1↑,⍵}~(⎕NC⍕target)∊0 9 ⍝ deal with name clashes 345 | :AndIf (,'#')≢,⍕target 346 | ('target="',(⍕target),'" exists already with ⎕NC=',(⍕⎕NC target),' and is protected')⎕SIGNAL(∨/' -protect'⍷options)/11 347 | ⎕EX target 348 | :EndIf 349 | ref←{9=⎕NC'⍵':⍵ ⋄ ⍎⍵}target 350 | :If ∨/'-nolink'⍷options 351 | res←2 ref.⎕FIX 1⊃(⎕NGET ⎕OPT'ContentType' 'APLCode')fl 1 352 | :Else 353 | res←2 ref.⎕FIX'file://',fl 354 | :EndIf 355 | :Else 356 | res←'*** Error ⎕FIXing code in ',fl,': ',NL 357 | res,←⎕DMX.(OSError{⍵,2⌽(×≢⊃⍬⍴2⌽⍺)/'") ("',⊃⍬⍴2⌽⍺}Message{⍵,⍺,⍨': '/⍨×≢⍺}⊃⍬⍴DM,⊂'') ⍝ CompCheck: ignore 358 | :EndTrap 359 | res ⎕SIGNAL('***'≡3↑⍕res)/11 360 | names,←⊂res 361 | :Case '.apla' 362 | :If 9=⎕SE.⎕NC'Link' 363 | :AndIf 3=⎕SE.Link.⎕NC'Import' 364 | :Trap DEBUG↓0 365 | {}⎕SE.Link.Import(⍎target)(fl) 366 | :Else 367 | res←'*** Error executing Link.Import (',target,') ',fl,':' 368 | res,←⎕DMX.(OSError{⍵,2⌽(×≢⊃⍬⍴2⌽⍺)/'") ("',⊃⍬⍴2⌽⍺}Message{⍵,⍺,⍨': '/⍨×≢⍺}⊃⍬⍴DM,⊂'') ⍝ CompCheck: ignore 369 | res ⎕SIGNAL 11 370 | :EndTrap 371 | names,←⊂({2 6::{2 6::⍵ ⋄ ⎕SE.Link.StripCaseCode 2⊃⎕NPARTS ⍵}⍵ ⋄ ⎕SE.Link.U.StripCaseCodePart ⍵}2⊃⎕NPARTS fl) ⍝ CompCheck: ignore 372 | :Else 373 | ('*** We need ]LINK to import ',fl)⎕SIGNAL 11 374 | :EndIf 375 | :EndSelect 376 | :EndIf 377 | :EndFor 378 | :EndIf 379 | :If ~0∊⍴names ⍝ if we found any names 380 | :Leave ⍝ do not bother searching SALT's workdirs! 381 | :EndIf 382 | :EndFor 383 | 384 | ∇ 385 | 386 | ∇ {r}←data Put name 387 | ⍝ Write data to file. r=number of bytes written. 388 | :Select |≡data 389 | :CaseList 0 1 390 | :Case 2 391 | data←⊃data 392 | :Case 3 393 | data←∊(⊃data),¨⊂NL 394 | :Else 395 | ⎕SIGNAL 11 396 | :EndSelect 397 | data←'UTF-8'⎕UCS data ⍝ get unsigned int so that we can write unicode 398 | data←¯128+256|128∘+data ⍝ convert to signed int 399 | r←data{z←⍺ ⎕NAPPEND(0 ⎕NRESIZE ⍵)83 ⋄ ⎕NUNTIE ⍵ ⋄ z}Nopen name 400 | ∇ 401 | 402 | ∇ tn←Nopen name 403 | :Trap 0 404 | tn←name ⎕NCREATE 0 405 | :Else 406 | tn←name ⎕NTIE 0 407 | :EndTrap 408 | ∇ 409 | 410 | ∇ R←{vers}∆TestClassic string;avu 411 | ⍝ ⎕AVU from Classic 12.1 ↓↓↓↓ ↓↓↓↓ 412 | R←'' 413 | avu←0 8 10 13 32 12 6 7 27 9 9014 619 37 39 9082 9077 95 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 1 2 175 46 9068 48 49 50 51 52 53 54 55 56 57 3 164 165 36 163 162 8710 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 4 5 253 183 127 9049 193 194 195 199 200 202 203 204 205 206 207 208 210 211 212 213 217 218 219 221 254 227 236 240 242 245 123 8364 125 8867 9015 168 192 196 197 198 9064 201 209 214 216 220 223 224 225 226 228 229 230 231 232 233 234 235 237 238 239 241 91 47 9023 92 9024 60 8804 61 8805 62 8800 8744 8743 45 43 247 215 63 8714 9076 126 8593 8595 9075 9675 42 8968 8970 8711 8728 40 8834 8835 8745 8746 8869 8868 124 59 44 9073 9074 9042 9035 9033 9021 8854 9055 9017 33 9045 9038 9067 9066 8801 8802 243 244 246 248 34 35 30 38 180 9496 9488 9484 9492 9532 9472 9500 9508 9524 9516 9474 64 249 250 251 94 252 96 8739 182 58 9079 191 161 8900 8592 8594 9053 41 93 31 160 167 9109 9054 9059 414 | :If 2=⎕NC'vers' 415 | :Select vers ⍝ use new version 416 | :Case 12 417 | avu←0 8 10 13 32 12 6 7 27 9 9014 619 37 39 9082 9077 95 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 1 2 175 46 9068 48 49 50 51 52 53 54 55 56 57 3 164 165 36 163 162 8710 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 4 5 253 183 127 9049 193 194 195 199 200 202 203 204 205 206 207 208 210 211 212 213 217 218 219 221 254 227 236 240 242 245 123 8364 125 8867 9015 168 192 196 197 198 9064 201 209 214 216 220 223 224 225 226 228 229 230 231 232 233 234 235 237 238 239 241 91 47 9023 92 9024 60 8804 61 8805 62 8800 8744 8743 45 43 247 215 63 8714 9076 126 8593 8595 9075 9675 42 8968 8970 8711 8728 40 8834 8835 8745 8746 8869 8868 124 59 44 9073 9074 9042 9035 9033 9021 8854 9055 9017 33 9045 9038 9067 9066 8801 8802 243 244 246 248 34 35 30 38 8217 9496 9488 9484 9492 9532 9472 9500 9508 9524 9516 9474 64 249 250 251 94 252 8216 8739 182 58 9079 191 161 8900 8592 8594 9053 41 93 31 160 167 9109 9054 9059 418 | :Case 13 ⍝ ⎕AVU from 14.0 onwards (the only difference is rtack which was (can't show here) before ) 419 | avu←0 8 10 13 32 12 6 7 27 9 9014 619 37 39 9082 9077 95 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 1 2 175 46 9068 48 49 50 51 52 53 54 55 56 57 3 8866 165 36 163 162 8710 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 4 5 253 183 127 9049 193 194 195 199 200 202 203 204 205 206 207 208 210 211 212 213 217 218 219 221 254 227 236 240 242 245 123 8364 125 8867 9015 168 192 196 197 198 9064 201 209 214 216 220 223 224 225 226 228 229 230 231 232 233 234 235 237 238 239 241 91 47 9023 92 9024 60 8804 61 8805 62 8800 8744 8743 45 43 247 215 63 8714 9076 126 8593 8595 9075 9675 42 8968 8970 8711 8728 40 8834 8835 8745 8746 8869 8868 124 59 44 9073 9074 9042 9035 9033 9021 8854 9055 9017 33 9045 9038 9067 9066 8801 8802 243 244 246 248 34 35 30 38 180 9496 9488 9484 9492 9532 9472 9500 9508 9524 9516 9474 64 249 250 251 94 252 96 8739 182 58 9079 191 161 8900 8592 8594 9053 41 93 31 160 167 9109 9054 9059 420 | :Case 14 421 | avu←0 8 10 13 32 12 6 7 27 9 9014 619 37 39 9082 9077 95 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 1 2 175 46 9068 48 49 50 51 52 53 54 55 56 57 3 8866 165 36 163 162 8710 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 4 5 253 183 127 9049 193 194 195 199 200 202 203 204 205 206 207 208 210 211 212 213 217 218 219 221 254 227 236 240 242 245 123 8364 125 8867 9015 168 192 196 197 198 9064 201 209 214 216 220 223 224 225 226 228 229 230 231 232 233 234 235 237 238 239 241 91 47 9023 92 9024 60 8804 61 8805 62 8800 8744 8743 45 43 247 215 63 8714 9076 126 8593 8595 9075 9675 42 8968 8970 8711 8728 40 8834 8835 8745 8746 8869 8868 124 59 44 9073 9074 9042 9035 9033 9021 8854 9055 9017 33 9045 9038 9067 9066 8801 8802 243 244 246 248 34 35 30 38 180 9496 9488 9484 9492 9532 9472 9500 9508 9524 9516 9474 64 249 250 251 94 252 96 8739 182 58 9079 191 161 8900 8592 8594 9053 41 93 31 160 167 9109 9054 9059 422 | :Else 423 | LogError'Unknow value for ClassicVersion, supported values are 12, 13, 14' 424 | →0 425 | :EndSelect 426 | :EndIf 427 | :If isChar string 428 | R←∪(~(⎕UCS,string)∊avu)/,string 429 | :EndIf 430 | ∇ 431 | 432 | ⍝ useful for CITA or DTest when dealing with WS FULL (from dfns): 433 | refs←{ ⍝ Vector of sub space references for ⍵. 434 | ⍺←⍬ ⋄ (⍴,⍺)↓⍺{ ⍝ default exclusion list. 435 | ⍵∊⍺:⍺ ⍝ already been here: quit. 436 | ⍵.(↑∇∘⍎⍨/⌽(⊂⍺∪⍵),↓⎕NL 9) ⍝ recursively traverse any sub spaces. 437 | }⍵ ⍝ for given starting ref. 438 | } 439 | 440 | swise←{ ⍝ Space wise 441 | ⍺⍺¨refs ⍵ ⍝ Apply to each space 442 | } 443 | 444 | ⍝─── these functions are removed with v1.7 - but in this release we'll issue a DEPRECATED msg when they are called (so that anyone that uses them becomes aware of needed update...) 445 | qNDELETE←qNEXISTS←qNGET←qMKDIR←qNPARTS←qJSON←qJSONi←qJSONe←qPUT←ListPre15←{((1⊃⎕SI),' is deprecated with DBuildTest 1.7 - please use native functions instead or v1.33 which still supports these')⎕SIGNAL 6} 446 | ⍝ need to replace niladic _FindDefine with a tradfn: 447 | ⎕fx'_FindDefine' '((1⊃⎕si),'' is deprecated with DBuildTest 1.7 - please use native functions instead or v1.33 which still supports these'')⎕signal 11' ⍝ niladic function... 448 | :endSection Compatibility 449 | 450 | :Section ADOC 451 | 452 | ∇ t←Describe 453 | t←1↓∊(⎕UCS 10),¨{⍵/⍨∧\(⊂'')≢¨⍵}Comments ⎕SRC ⎕THIS ⍝ first block of non-empty comment lines ⍝ CompCheck: ignore 454 | ∇ 455 | 456 | ∇ (n v d)←Version;f;s;z 457 | ⍝ Version of DBuildTest (3 elems: name version date) with a "proper number" for the version (formatted, though) 458 | (n v d)←SemVer 459 | v←{(2>+\⍵='.')/⍵}v 460 | ∇ 461 | 462 | ∇ (n v d)←SemVer;s;f 463 | ⍝ Version of DBuildTest (3 elems: name version date) using semantic versioning 464 | s←⎕SRC ⎕THIS 465 | f←Words⊃s ⍝ split first line 466 | n←2⊃f ⍝ ns name 467 | v←'V'~⍨⊃⌽f ⍝ version number 468 | d←1↓∊'-',¨3↑Words{w←⎕VFI¨⍵ ⋄ ⍵⊃⍨⊃{(,⍵)/,⍳⍴⍵}∊(∧/¨3↑¨1⊃¨w)}⌽Comments s ⍝ date (sorry, extra complicated - but getting date from last comment that has one) 469 | ∇ 470 | 471 | Words←{(' '≠⍵){⎕ML←3 ⋄ ⍺⊂⍵}⍵} 472 | Comments←{{1↓¨⍵/⍨∧\'⍝'=⊃∘{(∨\' '≠⍵)/⍵}¨⍵}1↓⍵} 473 | :EndSection ─────────────────────────────────────────────────────────────────────────────────── 474 | 475 | :Section UTILS 476 | 477 | ∇ r←APLVersion 478 | :Select 3↑⊃'.'⎕WG'APLVersion' 479 | :CaseList 'Lin' 'AIX' 'Sol' 480 | r←'*nix' 481 | :Case 'Win' 482 | r←'Win' 483 | :Case 'Mac' 484 | r←'Mac' 485 | :Else 486 | ∘∘∘ ⍝ unknown version 487 | :EndSelect 488 | ∇ 489 | 490 | ∇ r←isWin 491 | r←'Win'≡APLVersion 492 | ∇ 493 | 494 | ∇ R←dVersion 495 | ⍝ numeric version (maj.min) of DBuildTest (for comparison against the min. version given in the Dyalogtest element of a .dyalogtest) 496 | R←2⊃⎕VFI 2⊃Version 497 | ∇ 498 | 499 | Split←{dlb¨1↓¨(1,⍵∊⍺)⊂(⊃⍺),⍵} ⍝ Split ⍵ on ⍺, and remove leading blanks from each segment 500 | sSplit←{dlb¨1↓¨(1,(0=2|+\⍵='"')∧⍵∊⍺)⊂(⊃⍺),⍵} ⍝ string safe split (does not get confused by stuff enclosed in quotes) 501 | Splitb←{ 1↓¨(1,⍺)⊂'.',⍵} ⍝ Split of ⍵ where ⍺=1 (no dlb) 502 | SplitFirst←{dlb¨1↓¨(1,<\⍵=⍺)⊂⍺,⍵} ⍝ Split ⍵ on first occurence of ⍺, and remove leading blanks from each segment 503 | GetParam←{⍺←'' ⋄ dtb dlb(⌊/names⍳eis ⍵)⊃values,⊂⍺} ⍝ Get value of parameter 504 | ⍝unq←{(3>≢⍵)∨~isChar ⍵:⍵ ⋄ z←'""'≡⍵[1,≢⍵] ⋄ z↓(-z)↓⍵} 505 | ⍝↑↑↑unquote string (fails if we have a string like '"my long Path", -flag=" and my flag"') - but I don't think that's likely to happend with the current params 506 | ⍝↓↓↓ better: 507 | unq←{(3>≢⍵)∨~isChar ⍵:⍵ ⋄ ('^\s*"([^"]*)"'⎕R'\1')⍵} 508 | dlb←{(∨\' '≠⍵)/⍵} ⍝ delete leading blanks 509 | dtb←{(-{⍵⊥⍵}⍵=' ')↓⍵} ⍝ delete trailing blanks (DB) 510 | null←0 ⍝ UCMD switch not specified 511 | whiteout←{w←⍵ ⋄ ((w=⎕UCS 9)/w)←' ' ⋄ w} ⍝ convert whitespace to space 512 | isChar ←{0 2∊⍨10|⎕DR ⍵} ⍝ determine if argument's datatype is character 513 | _hasBitSet←{t←8⍴2 ⋄ 0<+/(t⊤⍺)∧t⊤⍵} ⍝ deal with bit flags (hardcoded maximum is 8) 514 | 515 | WIN←⎕SE.SALTUtils.WIN ⍝ running under Windows1 516 | 517 | ∇ r←∆CSV args;z;file;encoding;coltypes;num 518 | ⍝ Primitive ⎕CSV for pre v16 519 | ⍝ No validation, no options 520 | :Trap 2 ⍝ Syntax Error if ⎕CSV not available 521 | r←⎕CSV args ⍝ CompCheck: ignore 522 | :Else 523 | (file encoding coltypes)←args 524 | z←1⊃⎕NGET file 525 | z←1↓¨↑{(','=⍵)⊂⍵}¨',',¨z 526 | :If 0≠⍴num←(2=coltypes)/⍳⍴coltypes 527 | z[;num]←{⊃2⊃⎕VFI ⍵}¨z[;num] 528 | :EndIf 529 | r←z 530 | :EndTrap 531 | ∇ 532 | 533 | ⍝ from dfns: 534 | rmcm←{ ⍝ APL source with comments removed. 535 | cm←∨\(⍵='⍝')>≠\⍵='''' ⍝ mask of comments. 536 | ⎕UCS(cm×32)+(~cm)×⎕UCS ⍵ ⍝ blanks for comments in source matrix. 537 | } 538 | 539 | ∇ Init lvl;aplv1 540 | ⍝ Setup some names with information about the platform we're working on 541 | :If lvl≥0 542 | _Version←2⊃'.'⎕VFI 2⊃'.'⎕WG'APLVersion' 543 | ⍝ Version has all the details (major, minor, revision, *) 544 | ⍝ whereas DyaVersion is more readable with its simple numeric format 545 | DyaVersion←{2⊃⎕VFI(2>+\'.'=⍵)/⍵}2⊃'.'⎕WG'APLVersion' 546 | :EndIf 547 | :If lvl≥1 548 | :OrIf lvl=¯1 549 | _isClassic←Classic←82=⎕DR' ' 550 | _is32bit←~_is64bit←∨/'64'⍷aplv1←1⊃'.'⎕WG'APLVersion' 551 | _OS←3↑aplv1 ⍝ 12.1 does not know it... 552 | (_isWin _isLinux _isAIX _isMacOS _isSolaris)←'Win' 'Lin' 'AIX' 'Mac' 'Sol'∊⊂3↑_OS 553 | _isCITA←~0∊⍴2 ⎕NQ'.' 'GetEnvironment' 'CITATEST' 554 | _DotNet←1⊃GetDOTNETVersion 555 | NL←⎕UCS 10,⍨isWin/13 556 | :EndIf 557 | :If lvl≥2 558 | SetupCompatibilityFns ⍝ dedicated function avoid unneccessary execution of that code when loading the UCMD 559 | :EndIf 560 | ∇ 561 | 562 | ∇ r←base64 w 563 | ⍝ from dfns workspace 564 | r←{⎕IO ⎕ML←0 1 ⍝ Base64 encoding and decoding as used in MIME. 565 | chars←'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' 566 | bits←{,⍉(⍺⍴2)⊤⍵} ⍝ encode each element of ⍵ in ⍺ bits, 567 | ⍝ and catenate them all together 568 | part←{((⍴⍵)⍴⍺↑1)⊂⍵} ⍝ partition ⍵ into chunks of length ⍺ 569 | 0=2|⎕DR ⍵:2∘⊥∘(8∘↑)¨8 part{(-8|⍴⍵)↓⍵}6 bits{(⍵≠64)/⍵}chars⍳⍵ 570 | ⍝ decode a string into octets 571 | four←{ ⍝ use 4 characters to encode either 572 | 8=⍴⍵:'=='∇ ⍵,0 0 0 0 ⍝ 1, 573 | 16=⍴⍵:'='∇ ⍵,0 0 ⍝ 2 574 | chars[2∘⊥¨6 part ⍵],⍺ ⍝ or 3 octets of input 575 | } 576 | cats←⊃∘(,/)∘((⊂'')∘,) ⍝ catenate zero or more strings 577 | cats''∘four¨24 part 8 bits ⍵ 578 | }w 579 | ∇ 580 | 581 | ∇ b64←base64enc txt 582 | b64←base64'UTF-8'⎕UCS txt 583 | ∇ 584 | 585 | ∇ txt←base64dec b64 586 | txt←'UTF-8'⎕UCS base64 b64 587 | ∇ 588 | :EndSection ──────────────────────────────────────────────────────────────────────────────────── 589 | 590 | :Section TEST "DSL" FUNCTIONS 591 | 592 | ∇ {r}←l Assert b;cl;cc;t;v;nr;z 593 | nr←1↓⎕NR 2⊃⎕XSI ⍝ drop off header 594 | cl←⎕LC[2]⊃nr ⍝ the current line 595 | :If verbose ⍝ look for "verbose" in current ns or its parent 596 | ⎕←cl 597 | :EndIf 598 | →(l Check b)↓r←0 599 | t←nr[(⍳≢nr)∩⎕LC[2]-0 1 ¯1] ⍝ search exactly these 3 lines, avoiding INDEX ERRORs 600 | t←('⍝(.*)'⎕S'\1'⎕OPT('Mode' 'L'))t ⍝ search for text of comments 601 | cc←(¯1+cl⍳'⍝')↑cl 602 | v←'' 603 | :If 60≥≢⍕l 604 | :AndIf 60≥≢⍕b 605 | v←((⎕UCS 10),' left ',{'arg = "',(⍕⍵),'", ⎕DR=',(⍕⎕DR ⍵),', rho=',⍕⍴⍵}l),⎕UCS 10 606 | v,←('right ',{'arg = "',(⍕⍵),'", ⎕DR=',(⍕⎕DR ⍵),', rho=',⍕⍴⍵}b),⎕UCS 10 607 | :EndIf 608 | :If ∨/z←cc=⎕AV[60] ⍝ look for right tack as separator between reason & test (not using symbol directly ) 609 | :OrIf ∨/z←'IfNot'⍷cc 610 | t←(¯1+⊃##.where z)↑cc 611 | t←(1⊃⎕RSI)⍎t 612 | (t,v)⎕SIGNAL 777 613 | :ElseIf 0=≢t ⍝ no comment found on or around the crashing line 614 | :OrIf ∨/(1⊃t)⍷cl ⍝ don't add comment if it is on the line of the test! 615 | v ⎕SIGNAL 777 616 | :ElseIf 0<≢1⊃t 617 | ((1⊃t),v)⎕SIGNAL 777 618 | :Else 619 | v ⎕SIGNAL 777 620 | :EndIf 621 | ∇ 622 | 623 | 624 | IfNot←{ 625 | r←~⍵ 626 | r/⍺ 627 | } 628 | 629 | ∇ r←Test args;TID;timeout;ai;nl;i;quiet 630 | ⍝ run some tests from a namespace or a folder 631 | ⍝ switches: args.(filter setup teardown verbose) 632 | ⍝ result "r" is build in XTest (excute test) as global "r" gets updated. Can't return explicit result in XTest because we're running it in a thread. 633 | Init 2 634 | 'Dyalog APL 18.2 or later is required to run DTest'⎕SIGNAL(DyaVersion<18.2)/11 635 | 636 | i←quiet←0 ⍝ Clear/Log needs these 637 | Clear args.clear 638 | 639 | timeout←0 args.Switch'timeout' 640 | r←'' 641 | :If timeout>0 642 | ai←⎕AI[3]+timeout×1000 643 | TID←XTest&args 644 | :While ⎕AI[3],ZI2,<->,ZI2,<->,ZI2,<:>,ZI2,<:>,ZI3'⎕FMT 1 6⍴⎕TS),' *** DTest ',2⊃SemVer 664 | LOGSi←LOGS←3⍴⊂'' ⍝ use distinct variables for initial logs and test logs 665 | 666 | (verbose filter halt quiet trace timestamp order)←args.(verbose filter halt quiet trace ts order) 667 | :If (,quiet)≢(,1) 668 | ⎕←ThisTestID ⍝ this MUST go into the session because it marks the start of this test (useful to capture session.log later!) 669 | :EndIf 670 | 671 | repeat←{~isChar ⍵:⍵ ⋄ ⍬⍴2⊃⎕VFI ⍵}args.repeat 672 | loglvl←{~isChar ⍵:⍵ ⋄ ⍬⍴2⊃⎕VFI ⍵}args.loglvl 673 | order←{~isChar ⍵:⍵ ⋄ ⍬⍴2⊃⎕VFI ⍵}order 674 | off←{~isChar ⍵:⍵ ⋄ ⍬⍴2⊃⎕VFI ⍵}args.off 675 | :If halt 676 | ⎕TRAP←0 'S' 677 | :EndIf ⍝ Defeat UCMD trapping 678 | 679 | repeat←1⌈repeat 680 | file←'' 681 | args.coverage_subj←null 682 | args.coverage_ignore←{¯1↓∊((⊂⍕⍵),¨'.',¨⍵.⎕NL ¯3 ¯4),¨','}⎕THIS 683 | WSFULL←0 ⍝ indicates if we were hit by WS FULL 684 | ⎕SE.DTEST_COUNTER_OF_CALLS_TO_CHECK←0 685 | 686 | 687 | :If 0∊⍴args.Arguments 688 | :AndIf 9≠#.⎕NC source←⊃args.Arguments←,⊂'Tests' 689 | r←'An argument is required - see ]dtest -? for more information.' ⋄ →0 690 | :ElseIf 9=#.⎕NC source←1⊃args.Arguments ⍝ It's a namespace 691 | ns←#⍎source 692 | TESTSOURCE←⊃1 ⎕NPARTS'' 693 | base←source 694 | :Else ⍝ Not a namespace 695 | :If ⎕NEXISTS f←source ⍝ Argument is a file 696 | :OrIf ⎕NEXISTS f←source,'.dyalogtest' 697 | :OrIf ⎕NEXISTS f←WSFOLDER,source 698 | :OrIf ⎕NEXISTS f←WSFOLDER,source,'.dyalogtest' 699 | :OrIf ⎕NEXISTS f←WSFOLDER,'Tests/',source 700 | :OrIf ⎕NEXISTS f←WSFOLDER,'Tests/',source,'.dyalogtest' 701 | :OrIf ⎕NEXISTS f←∊1 ⎕NPARTS source ⍝ deal with relative names for folders 702 | :OrIf ⎕NEXISTS f←∊1 ⎕NPARTS source,'.dyalogtest' ⍝ or individual tests 703 | file←f ⍝ assign this variable which is needed by LogError 704 | (TESTSOURCE z extension)←1 ⎕NPARTS f 705 | base←z 706 | 'ns'⎕NS'' ⍝ create temporary namespace to run tests in 707 | :If 2=type←GetFilesystemType f ⍝ it's a file 708 | :If '.dyalogtest'≡lc extension ⍝ That's a suite 709 | :If null≡args.suite 710 | args.suite←f 711 | :EndIf 712 | f←¯1↓TESTSOURCE ⋄ type←1 ⍝ Load contents of folder 713 | :Else ⍝ Arg is a source file - load it 714 | :If filter≢null 715 | LogTest'Can''t run test with file argument AND -filter switch!' 716 | LOGSi←LOGS 717 | →FAIL 718 | :EndIf 719 | :Trap (DEBUG∨halt)↓0 720 | filter←∊LoadCode source ns 721 | :If args.tests≡0 722 | args.tests←filter ⍝ transfer into tests, as filtering could be ambigous and we wouldn't want to run more than required... 723 | :EndIf 724 | f←¯1↓TESTSOURCE ⋄ type←1 ⍝ Load contents of folder 725 | :Else 726 | msg←'Error loading test from folder "',source,'"',NL 727 | LogError msg,⎕DMX.(OSError{⍵,2⌽(×≢⊃⍬⍴2⌽⍺)/'") ("',⊃⍬⍴2⌽⍺}Message{⍵,⍺,⍨': '/⍨×≢⍺}⊃⍬⍴DM,⊂'') ⍝ CompCheck: ignore 728 | →endPrep1 729 | :EndTrap 730 | :EndIf 731 | :EndIf 732 | 733 | :If 1=type ⍝ deal with directories in f 734 | TESTSOURCE←∊1 ⎕NPARTS f,(~'/\'∊⍨⊃⌽f)/'/' ⍝ use it accordingly! (and be sure it ends with dir sep)' 735 | files←('*.dyalog'ListFiles f)[;1] 736 | files,←('*.aplf'ListFiles f)[;1] ⍝ .aplf extension! 737 | :If 0=≢files 738 | LogError'Path "',TESTSOURCE,'" did not contain any test_-files!' 739 | LOGSi←LOGS 740 | →FAIL 741 | :EndIf 742 | :For f :In files 743 | :Trap (DEBUG∨halt)↓0 744 | LoadCode f ns 745 | :Else 746 | msg←'Error loading code from file "',f,'"' 747 | LogError msg,⎕DMX.(OSError{⍵,2⌽(×≢⊃⍬⍴2⌽⍺)/'") ("',⊃⍬⍴2⌽⍺}Message{⍵,⍺,⍨': '/⍨×≢⍺}⊃⍬⍴DM,⊂'') ⍝ CompCheck: ignore 748 | :EndTrap 749 | :EndFor 750 | ⍝:if null≡args.tests 751 | ⍝ args.tests←ns.⎕nl¯3 752 | ⍝ :endif 753 | :If verbose 754 | 0 Log(⍕1↑⍴files),' file',('s'/⍨1<≢files),' loaded from ',source 755 | :EndIf 756 | :If null≡args.suite ⍝ if no suite is given 757 | :If null≡args.setup 758 | nl←ns.⎕NL ¯3 759 | mask←('setup_'⍷↑nl)[;1] 760 | :If 0<+/mask 761 | args.setup←1↓∊' ',¨mask/nl 762 | :EndIf 763 | :If 1<+/mask 764 | :If 2=GetFilesystemType f ⍝ single file given 765 | Log'No -suite nor -setup selected - running test against all setups!' 766 | :Else ⍝ directory 767 | Log'No -suite nor -setup selected - running all tests in "',f,'" against all setups!' 768 | :EndIf 769 | :EndIf 770 | :If ~0∊⍴v←('teardown_'⍷↑nl)[;1]/nl←ns.⎕NL ¯3 771 | args.teardown←¯1↓∊v,¨' ' 772 | :EndIf 773 | :EndIf 774 | :EndIf 775 | :EndIf 776 | :Else 777 | :If args.init ⍝ can we init it? 778 | :AndIf ∧/0<∊⍴¨1↑¨(TESTSOURCE z extension)←1 ⎕NPARTS source ⍝ did user give a file spec? then try to create it! 779 | :If ~⎕NEXISTS TESTSOURCE ⍝ does directory exist? 780 | {}3 ⎕MKDIR TESTSOURCE 781 | :EndIf 782 | :If '.dyalogtest'≡lc extension 783 | templ←('DyalogTest : ',2⊃SemVer)'ID :' 'Description:' '' 'Setup :' 'Teardown:' '' 'Test:' 784 | :Else 785 | templ←('r←',z,' dummy;foo')'r←''''' ':If .. Check ..'(' →0 Because ''test failed'' ⋄ :EndIf') 786 | :EndIf 787 | (⊂templ)⎕NPUT source 788 | Log'Initialised ',source 789 | →0 790 | :EndIf 791 | :If halt ⍝ we found an error and need to stop 792 | ⎕←'"',source,'" is neither a namespace nor a folder or a .dyalogtest file.' 793 | (⎕LC[1]+1)⎕STOP 1⊃⎕XSI 794 | :EndIf 795 | LogTest'"',source,'" is neither a namespace nor a folder or a .dyalogtest file.' 796 | (TESTSOURCE base)←2↑1 ⎕NPARTS source 797 | LOGSi←LOGS 798 | →FAIL 799 | :EndIf 800 | :EndIf 801 | endPrep1: 802 | 803 | :If null≢suite←args.suite ⍝ Is a test suite defined? 804 | ⍝ Merge settings 805 | overwritten←⍬ 806 | v←LoadTestSuite suite 807 | :If ~1⊃v 808 | LogError'*** error loading suite "',suite,'": ',2⊃v 809 | :Else 810 | sargs←2⊃v 811 | :For v :In (sargs.⎕NL ¯2)∩args.⎕NL ¯2 ⍝ overlap? 812 | :If null≢args⍎v 813 | overwritten,←⊂v 814 | ⍎'sargs.',v,'←args.',v 815 | :EndIf 816 | :EndFor 817 | 'args'⎕NS sargs ⍝ merge 818 | :If 0≠⍴overwritten 819 | 0 Log'*** warning - test suite overridden by modifiers: ',,⍕overwritten 820 | :EndIf 821 | :EndIf 822 | :EndIf 823 | 824 | :If args.SuccessValue≢0 825 | SuccessValue←{0::⍵ ⋄ ⍎⍵}args.SuccessValue 826 | :EndIf 827 | SuccessValue←{ 828 | 'json!'≡⎕C 5↑⍵:∇ ⎕JSON 5↓⍵ 829 | 'apl!'≡⎕C 4↑⍵:∇⍎4↓⍵ 830 | 'b64!'≡⎕C 4↑⍵:∇ base64dec 4↓⍵ 831 | ⍵ 832 | }SuccessValue 833 | ⍝ Establish test DSL in the namespace 834 | ⍝ :If halt=0 835 | ⍝ ns.Check←≢ ⍝ CompCheck: ignore 836 | ⍝ :Else 837 | 'ns'⎕NS'Check' 838 | ⍝ :EndIf 839 | 'ns'⎕NS'Because' 'Fail' 'IsNotElement' 'RandomVal' 'tally' 'eis' 'Assert' 'IfNot' 'base64' 'base64dec' 'base64enc' 840 | ⍝ transfer some status vars into ns 841 | 'ns'⎕NS'verbose' 'filter' 'halt' 'quiet' 'trace' 'timestamp' 'order' 'off' 842 | 843 | ns.Log←{⍺←{⍵} ⋄ ⍺ ##.LogTest ⍵} ⍝ ⍺←rtack could cause problems with classic... 844 | :If args.tests≢0 845 | orig←fns←(','Split args.tests)~⊂'' 846 | nl←ns.⎕NL ¯3 847 | fns←{w←⍵ ⋄ ((w='?')/w)←'.' ⋄ ((w='*')/w)←⊂'.*' ⋄ ∊⍵}¨fns ⍝ replace bare * wildcard with .* to and ? with . make it valid regex 848 | fns←1⌽¨'$^'∘,¨fns ⍝ note ^ is shift-6, not the APL function ∧ 849 | t←1 850 | :If 0∊⍴matches←↑fns ⎕S{⍵.(Block PatternNum)}ns.⎕NL ¯3 ⍝ CompCheck: ignore 851 | LogError'*** function(s) not found: ',,⍕t/orig 852 | fns←⍬ 853 | :Else 854 | :If ∨/t←~(⍳⍴fns)∊1+∪matches[;2] 855 | LogError'*** function(s) not found: ',,⍕t/orig 856 | :EndIf 857 | fns←∪matches[⍋matches[;2];1] 858 | :EndIf 859 | :Else ⍝ No functions selected - run all named test_* 860 | fns←{⍵⌿⍨(⊂'test_')≡¨5↑¨⍵}ns.⎕NL ¯3 861 | :If 0=≢fns 862 | LogError'*** no functions match pattern "test_*"' 863 | LOGSi←LOGS 864 | →FAIL 865 | :EndIf 866 | :EndIf 867 | filter←{w←⍵ ⋄ ~∨/'?*'∊⍵:⍵ ⋄ ((w='?')/w)←'.' ⋄ ((w='*')/w)←⊂'.*' ⋄ ∊⍵}filter 868 | :If null≢filter 869 | :AndIf 0=≢fns←filter ⎕S'%'⊢fns 870 | LogError'*** no functions match filter "',filter,'"' 871 | LOGSi←LOGS 872 | →FAIL 873 | :EndIf 874 | 875 | :If 0<≢args.setup 876 | :AndIf null≢setups←args.setup 877 | setups←' 'Split args.setup 878 | :Else 879 | setups←null 880 | :EndIf 881 | 882 | r←'' ⍝ must be global here, it is the result of the calling fn () 883 | start0←⎕AI[3] 884 | :Select ,order 885 | :Case ,0 ⍝ order=0: random (or reproduce random from file) 886 | order←(('order',⍕≢fns)RandomVal 2⍴≢fns)∩⍳≢fns 887 | :Case ,1 888 | order←⍳≢fns ⍝ 1: sequential 889 | :Else 890 | order←order{(⍺∩⍵),⍵~⍺}⍳≢fns ⍝ numvec: validate and use that order (but make sure every test gets executed!) 891 | :EndSelect 892 | LOGSi←LOGS 893 | :If null≢args.coverage ⍝ if switch is set 894 | :AndIf (1↑1⊃⎕VFI⍕args.coverage)∨1<≢args.coverage ⍝ and we have either numeric value for switch or a longer string 895 | :AndIf 0=⎕NC'CoCo' ⍝ only neccessary if we don't have an instance yet... 896 | :If 0=≢home←2 ⎕NQ #'GetEnvironment' 'DTEST_CODECOVERAGE_PATH' ⍝ no env var set 897 | ⍝ we can't yet rely on Tatin (too much reorganization of THIS code), so let's see if we can use it to bring in CodeCoverage (w/o affecting the environment)^ 898 | tmpName←'⎕se.t',⍕1+≢'t'⎕SE.⎕NL 2 3 9 899 | z←{0::'' ⋄ ⎕SE.UCMD ⍵}'TATIN.LoadPackages aplteam-CodeCoverage-',CodeCoverageVersion,' ',tmpName ⍝ try to load it with ucmd 900 | :If ⊃'1 package loaded into'⍷z 901 | CodeCoverage←tmpName⍎'CodeCoverage' ⍝ establish CoCo here as a ref to the one we loaded into tmpName 902 | →gotit ⍝ check the message from ucmd 903 | :EndIf 904 | :For path :In ucmdf←(⊃⎕SE.SALTUtils.PATHDEL)(≠⊆⊢)⎕SE.SALT.Set'cmddir' ⍝ search all UCMD folders 905 | :Trap 22 906 | lib←0 1 ⎕NINFO path,'/aplteam-CodeCoverage-',CodeCoverageVersion ⍝ for a subfolder "aplteam-CodeCoverage-" with specific version 907 | :If 0<¯1↑⍴lib 908 | home←1⊃lib 909 | :Leave 910 | :EndIf 911 | :EndTrap 912 | :EndFor 913 | :EndIf 914 | ccf←home∘,¨'/CodeCoverage.aplc' '/APLSource/CodeCoverage.aplc'('/aplteam-CodeCoverage-',CodeCoverageVersion,'/APLSource/CodeCoverage.aplc') ⍝ also look in plausible subfolders 915 | :If 0=≢home 916 | :OrIf ~∨/z←⎕NEXISTS¨ccf 917 | LogError'Unable to find folder with CodeCoverage files for version ',CodeCoverageVersion,', please download it from https://github.com/aplteam/CodeCoverage/releases and save folder "aplteam-CodeCoverage',CodeCoverageVersion,'" in one of the UCMD folders ',(∊' - '∘,¨ucmdf),' or save it anywhere and set DTEST_CODECOVERAGE_PATH appropriately' 918 | setupok←0 919 | →END 920 | :EndIf 921 | ccf←⊃z/ccf 922 | 2 ⎕FIX'file://',ccf 923 | ⎕←'loaded coco from ',home 924 | gotit: 925 | :EndIf 926 | :For run :In ⍳repeat 927 | :If verbose∧repeat>1 928 | 0 Log'run #',(⍕run),' of ',⍕repeat 929 | :EndIf 930 | :For setup :In (,setups)[('setups',⍕≢setups)RandomVal 2⍴≢setups] ⍝ randomize order of setups 931 | steps←0 932 | start←⎕AI[3] 933 | LOGS←3⍴⊂'' 934 | :If verbose 935 | :AndIf setup≢null 936 | :AndIf setup≢,1 937 | r,←⊂'For setup = ',setup 938 | :EndIf 939 | :If ~setupok←(⊂f←setup)∊(,1)null 940 | :If 3=ns.⎕NC f ⍝ function is there 941 | :If verbose 942 | 0 Log'running setup: ',f 943 | :EndIf 944 | (trace/1)ns.⎕STOP f 945 | :Trap (~halt∨trace)/0 946 | :If 0=1 2⊃ns.⎕AT f ⍝ niladic setup 947 | f LogTest z←ns⍎f 948 | :Else 949 | f LogTest z←(ns⍎f)⍬ 950 | :EndIf 951 | setupok←z≡SuccessValue 952 | :Else 953 | msg←'Error executing setup "',f,'": ' 954 | msg,←(⎕JSON ⎕OPT'Compact' 0)⎕DMX 955 | :If 90=⎕EN 956 | :Trap 0 957 | msg,'** Exception details: ',⍕⎕EXCEPTION 958 | :EndTrap 959 | :EndIf 960 | LogError msg 961 | setupok←0 962 | :EndTrap 963 | :Else 964 | :If halt 965 | ⎕TRAP←0 'S' ⋄ (⎕LC[1]+1)⎕STOP 1⊃⎕XSI ⋄ :EndIf 966 | LogTest'-setup function not found: ',f 967 | setupok←0 968 | :EndIf 969 | :EndIf 970 | 971 | →setupok↓END 972 | ⍝ after setup, make sure to start CodeCoverage (if modifier is set) - once only... 973 | :If null≢args.coverage ⍝ if switch is set 974 | :AndIf (1↑1⊃⎕VFI⍕args.coverage)∨1<≢args.coverage ⍝ and we have either numeric value for switch or a longer string 975 | :AndIf 0=⎕NC'CoCo' ⍝ only neccessary if we don't have an instance yet... 976 | :If null≡subj←args.coverage_subj 977 | :If 0<≢subj←#.⎕NL ¯9 978 | subj←¯1↓∊(⊂'#.'),¨subj,¨',' 979 | :EndIf 980 | subj,←',',⍕ns 981 | :EndIf 982 | CoCo←⎕NEW CodeCoverage(,⊂subj) 983 | CoCo.Info←'Report created by DTest ',(2⊃SemVer),' which was called with these arguments: ',⊃¯2↑⎕SE.Input 984 | :If 1<≢args.coverage 985 | :AndIf (⎕DR' ')=⎕DR args.coverage 986 | :If ∨/'\/'∊args.coverage ⍝ if the argument looks like a filename (superficial test) 987 | CoCo.filename←args.coverage 988 | :Else ⍝ otherwise we assume it is the name of the instance of an already running coverag-analysis 989 | CoCo.filename←(⍎args.coverage).filename 990 | :EndIf 991 | CoCo.NoStop←1 992 | :Else 993 | CoCo.filename←(739⌶0),,',ZI4,ZI2,ZI2,ZI2,ZI2,ZI3'⎕FMT 1 6⍴⎕TS 994 | CoCo.NoStop←0 995 | :EndIf 996 | :If 0=≢ignore←args.coverage_ignore 997 | ⍝ignore←∊(⊂⍕⎕THIS),¨'.',¨(⎕THIS.⎕NL ¯3 4),¨',' 998 | ignore←∊{(⊂⍕⍵),¨'.',¨(⍵.⎕NL ¯3 ¯4),¨','}⎕SE.input.c 999 | :EndIf 1000 | ignore,←(((0<≢ignore)∧','≠¯1↑ignore)⍴','),¯1↓∊(⊂(⍕⎕THIS),'.ns.'),¨('Check' 'base64' 'base64dec' 'base64enc' 'Because' 'Fail' 'IsNotElement' 'RandomVal' 'tally' 'eis' 'Log' 'Assert' 'IfNot'),¨',' 1001 | CoCo.ignore←ignore 1002 | CoCo.Start ⍬ 1003 | :EndIf 1004 | 1005 | 1006 | :If verbose 1007 | :AndIf 1<≢fns 1008 | 0 Log'running ',(⍕1↑⍴fns),' tests'↓⍨¯1×1=↑⍴fns 1009 | :EndIf 1010 | :For f :In fns[order] 1011 | steps+←1 1012 | :If verbose 1013 | 0 Log'running: ',f 1014 | :EndIf 1015 | (trace/1)ns.⎕STOP f 1016 | :Trap (~halt∨trace)/0 1017 | :If 0=1 2⊃ns.⎕AT f 1018 | f LogTest(ns⍎f) 1019 | :Else 1020 | f LogTest((ns⍎f)⍬) ⍝ avoid additional line with title of function 1021 | :EndIf 1022 | :Case 777 ⍝ Assertion failed 1023 | f LogTest'Assertion failed: ',,∊⎕DMX.DM[⍳2],¨⊂NL 1024 | :Else 1025 | en←⎕EN ⍝ save error no before it gets overwritten 1026 | msg←'Error executing test "',f,'": ' 1027 | msg,←(⎕JSON ⎕OPT'Compact' 0)⎕DMX ⍝ CompCheck: ignore 1028 | :If WSFULL←en=1 ⍝ special handling for WS FULL 1029 | msg,←NL,'⎕WA=',(⍕⎕WA) 1030 | msg,←NL,'The 20 largets objects found in the workspace:',NL 1031 | :Trap 1 1032 | res←⊃⍪/{((⊂⍕⍵),¨'.',¨↓nl),[1.5]⍵.⎕SIZE nl←⍵.⎕NL⍳9}swise ns ⍝ CompCheck: ignore 1033 | res←res[(20⌊1↑⍴res)↑⍒res[;2];] 1034 | msg←msg,∊((↑res[;1]),'CI18'⎕FMT res[;,2]),⊂NL 1035 | :Else 1036 | msg,←'Error while generating that report: ',NL,∊⎕DMX.DM,¨⊂NL 1037 | :EndTrap 1038 | 1039 | :EndIf 1040 | ⍝ LogError msg 1041 | f LogTest msg 1042 | :EndTrap 1043 | :EndFor 1044 | 1045 | :If null≢f←args.teardown 1046 | :If 3=ns.⎕NC f ⍝ function is there 1047 | :If verbose 1048 | 0 Log'running teardown: ',f 1049 | :EndIf 1050 | (trace/1)ns.⎕STOP f 1051 | :Trap (~halt∨trace)/0 777 1052 | :If 0=1 2⊃ns.⎕AT f 1053 | f LogTest(ns⍎f) 1054 | :Else 1055 | f LogTest(ns⍎f)⍬ 1056 | :EndIf 1057 | :Else 1058 | msg←'Error executing teardown "',f,'" :' 1059 | msg,←(⎕JSON ⎕OPT'Compact' 0)⎕DMX ⍝ CompCheck: ignore 1060 | LogError msg 1061 | :EndTrap 1062 | :Else 1063 | :If halt 1064 | ⎕TRAP←0 'S' ⋄ (⎕LC[1]+1)⎕STOP 1⊃⎕XSI ⋄ :EndIf 1065 | LogError'-teardown function not found: ',f 1066 | :EndIf 1067 | :EndIf 1068 | 1069 | END: 1070 | :For j :In ⍳3 1071 | :If ~0∊⍴j⊃LOGS 1072 | t←(j⊃'Infos' 'Warnings' '*** Errors'),' logged' 1073 | :If 2=⎕NC'setup' 1074 | :AndIf (setup≢null) 1075 | t,←' with setup "',setup,'"' 1076 | :EndIf 1077 | t,←':',⎕UCS 13 1078 | t,←(2⍴' '){w←⍵ ⋄ w←(~(⎕UCS 13 10)⍷w)/w ⋄ w←(~(⎕UCS 10 13)⍷w)/w ⋄ ((w∊⎕UCS 10 13)/w)←⊂(⎕UCS 13),⍺ ⋄ ∊w}{1=≡⍵:⍵ ⋄ ¯1↓∊⍵,¨⎕UCS 10}j⊃LOGS 1079 | r,←⊂t 1080 | :EndIf 1081 | :EndFor 1082 | :If 0∊⍴3⊃LOGS 1083 | r,←(quiet≡null)/⊂' ',(((setup≢null)∧1≠1↑⍴setups)/setup,': '),(⍕steps),' test',((1≠steps)/'s'),' (=',(⍕⎕SE.DTEST_COUNTER_OF_CALLS_TO_CHECK),' calls to "Check" or "Assert") passed in ',(1⍕0.001×⎕AI[3]-start),'s' 1084 | 1(⎕NDELETE ⎕OPT'Wildcard' 1)TESTSOURCE,'*.rng.txt' ⍝ delete memorized random numbers when tests succeeded 1085 | :ElseIf 2=⎕NC'start' 1086 | r,←⊂'Time spent: ',(1⍕0.001×⎕AI[3]-start),'s' 1087 | :EndIf 1088 | :EndFor ⍝ Setup 1089 | :EndFor ⍝ repeat 1090 | r,←((1<≢setups)∧quiet≡null)/⊂'Total Time spent: ',(1⍕0.001×⎕AI[3]-start0),'s' 1091 | :If ~0∊⍴3⊃LOGS 1092 | :AndIf ~0∊⍴order 1093 | r,←⊂'-order="',(⍕order),'"' 1094 | :EndIf 1095 | :If args.coverage≢null 1096 | :If 9=⎕NC'CoCo' 1097 | ⍝ :If 0=CoCo.⎕NC'NoStop' 1098 | ⍝ :OrIf CoCo.NoStop=0 1099 | CoCo.Stop ⍬ ⍝ must stop anyway, as this would gather data and write it to file 1100 | ⍝ :EndIf 1101 | r1←CoCo.Finalise ⍬ 1102 | ⎕EX'r2' 1103 | :If args.coverage≡1 ⍝ if we had a "simple run" (not collected into a file) 1104 | r2←CoCo.(1 ProcessDataAndCreateReport filename) ⍝ we can now process data from that run-... 1105 | :EndIf 1106 | tie←r1 ⎕FSTIE 0 1107 | tab←⎕FREAD tie,10 1108 | ⎕FUNTIE tie 1109 | CoCo.res←res←⌊0.5+100×÷/+⌿≢¨tab[;2 4] 1110 | r,←⊂'Coverage = ',(⍕res),'%' 1111 | :If 2=⎕NC'r2' ⍝ if we have processed data 1112 | r,←⊂']open ',r2,' ⍝ to see coverage details...' ⍝ let the user see it! 1113 | :EndIf ⍝ otherwise the calling environment will have tu use shared CoCo.AggregateCoverageDataAndCreateReport 1114 | :If 2=⎕NC'tmpName' 1115 | ⎕EX tmpName 1116 | :EndIf 1117 | :EndIf 1118 | :EndIf 1119 | →FAIL2 ⍝ skip adding LOGS to r (we've done that before already and only need the code below if something made us →FAIL) 1120 | FAIL: 1121 | ⍝ ⎕←'≢LOGS=',≢¨LOGS 1122 | :For j :In ⌽⍳3 1123 | :If ~0∊⍴j⊃LOGS 1124 | r,←(⊂(j⊃'Info' 'Warning' 'Error'),'s:'),' '∘,¨j⊃LOGS 1125 | :EndIf 1126 | :EndFor 1127 | LOGS←3⍴⊂'' ⍝ reset LOGS, it's in r now... 1128 | FAIL2: 1129 | LOGS←LOGSi,¨LOGS ⍝ prepend initial logs 1130 | r←↑⎕SE.Dyalog.Utils.layoutText ¯1↓∊r,¨⎕UCS 13 1131 | 1132 | :If off>0 1133 | :OrIf loglvl>0 1134 | :If 0≢args.testlog 1135 | :If (⎕DR' ')=⎕DR args.testlog 1136 | :AndIf 0<≢args.testlog 1137 | logFile←args.testlog 1138 | :If ''≡1⊃⎕NPARTS logFile 1139 | :AndIf ~∨/'\/'∊logFile 1140 | logFile←TESTSOURCE,logFile 1141 | :EndIf 1142 | :If ~'.'∊logFile ⍝ make sure we have an extension 1143 | logFile,←'.log' ⍝ and it's ".log" 1144 | :EndIf 1145 | :Else 1146 | logFile←TESTSOURCE,base,'.log' 1147 | :EndIf 1148 | :Else 1149 | logFile←TESTSOURCE,base,'.log' 1150 | :EndIf 1151 | 1152 | logBase←({∊(1⊃⎕NPARTS ⍵),{⍵[⍳¯1+⍵⍳'.']}∊1↓⎕NPARTS ⍵}logFile),'.' 1153 | :If (loglvl _hasBitSet 16)∧0<⍴3⊃LOGS 1154 | :OrIf loglvl _hasBitSet 8 1155 | log←⎕SE ⎕WG'Log' 1156 | ⍝ use progressive iota to find new log in old log and remove the common parts (simple ∊ is not good enough...) 1157 | ⍝ z←+/∧\{⍵=⍵[1]+0,⍳¯1+≢⍵}∆OldLog{((≢⍺)⍴⍋⍋⍺⍳⍺⍪⍵)⍳(≢⍵)⍴⍋⍋⍺⍳⍵⍪⍺}log 1158 | z←∆OldLog _cita.NrOfCommonLines log 1159 | log←z↓log 1160 | log←∊log,¨⊂NL 1161 | 1162 | ⍝ :If 0<≢i←{⍵/⍳tally ⍵}ThisTestID⍷log 1163 | ⍝ log←(i-1)↓log 1164 | ⍝ :EndIf 1165 | (⊂log)⎕NPUT(logBase,'session.log')1 1166 | :EndIf 1167 | 1168 | :For j :In ⍳2 1169 | :If ~0∊⍴j⊃LOGS 1170 | :If loglvl _hasBitSet j⊃2 4 1171 | (⊂i⊃LOGS)⎕NPUT logBase,(j⊃'info.log' 'warn.log') 1172 | :EndIf 1173 | :EndIf 1174 | :EndFor 1175 | 1176 | :If loglvl _hasBitSet 32 1177 | res←⎕NS'' 1178 | res.rc←20+(~0∊⍴3⊃LOGS)+WSFULL 1179 | res.(LogInfo LogWarn LogError)←LOGS 1180 | :If 9=⎕NC'CoCo' 1181 | res.CoveragePercent←CoCo.res 1182 | :EndIf 1183 | :If 2=⎕NC'⎕se._cita._memStats' 1184 | res.cpu23←⎕SE._cita.∆cpu 1185 | res.memStats←⎕SE._cita._memStats 1186 | :EndIf 1187 | (⊂(⎕JSON ⎕OPT('Compact' 0)('HighRank' 'Split'))res)⎕NPUT(logFile,'.json')1 ⍝ CompCheck: ignore 1188 | :EndIf 1189 | 1190 | :If ~0∊⍴3⊃LOGS 1191 | :AndIf (off>0)∨loglvl _hasBitSet 1 1192 | :Trap 0 1193 | (⊂∊(3⊃LOGS),¨⊂NL)⎕NPUT logFile 1 1194 | :Else 1195 | ⎕←'Error writing logFile' 1196 | ⎕←(⎕JSON ⎕OPT'Compact' 0)⎕DMX 1197 | :EndTrap 1198 | :If off=1 1199 | rc←21+WSFULL 1200 | ⎕OFF rc 1201 | :EndIf 1202 | :EndIf 1203 | 1204 | :If off=1 1205 | ⎕OFF 20 1206 | :EndIf 1207 | :EndIf 1208 | ∇ 1209 | 1210 | 1211 | ∇ msg Fail value 1212 | msg ⎕SIGNAL(1∊value)/777 1213 | ∇ 1214 | 1215 | ∇ line←line Because msg;si;fn 1216 | ⍝ set global "r", return branch label 1217 | :If 0=⎕NC'r' 1218 | r←'' 1219 | :EndIf 1220 | si←'' 1221 | :If 0=≢fn←('((?!\d)[\wÀ-ÖØ-Ýßà-öø-üþ∆⍙\x{24b6}-\x{24cf}]+)\[\d+]'⎕S'\1')msg ⍝ anything looking like function[lc] already in msg? (rx by AB) 1222 | :AndIf ~3∊∊⎕NC¨fn ⍝ then do not include it again... 1223 | si←(2⊃⎕XSI),'[',(⍕2⊃⎕LC),']: ' 1224 | :EndIf 1225 | r←r,((~0∊≢r)/⎕UCS 10),si,msg 1226 | ∇ 1227 | 1228 | ∇ r←expect Check got;desc;fv;show 1229 | ⎕SE.DTEST_COUNTER_OF_CALLS_TO_CHECK+←1 1230 | :If r←expect≢got 1231 | :AndIf ##.halt 1232 | show←{ 1233 | fv←1+80≥≢,⍕⍵ ⍝ can we sow "full value" (or is it too long? 1234 | e←0∊⍴⍵ ⍝ is it empty? 1235 | s←(⎕DR ⍵)∊80 82 160 320 1236 | t←1+s+2×326=⎕DR ⍵ 1237 | (e/'empty '),(t⊃'numeric' 'character' 'pointer (nested)'),' ',((1+2⌊⍴⍴⍵)⊃'scalar' 'vector' 'array'),(~e)/((0<⍴⍴⍵)/' of shape=',(⍕⍴⍵)),(fv⊃'. Preview=' ' with value='),⍎fv⊃'80↑⍕,⍵' '⍕,⍵' 1238 | } 1239 | ⎕←' TEST SUSPENDED! ───────────────────────────────────────────────────────────' 1240 | ⎕←'expect≢got:' 1241 | ⎕←'expect=',show expect 1242 | ⎕←'got=',show got 1243 | ⎕←'⍝ examine variables or into calling function' 1244 | :Trap 3 1245 | ⍝ INDEX ERROR possible if we can't get the ⎕NR (for example, if called by a class member) - though this should be fixed now... 1246 | ⎕←'call: ',(2⊃⎕XSI),'[',(⍕2⊃⎕LC),'] ',##.dtb(1+2⊃⎕LC)⊃↓(1⊃⎕RSI).(180⌶(2⊃⎕XSI)) 1247 | :EndTrap 1248 | (1+⊃⎕LC)⎕STOP 1⊃⎕XSI ⍝ stop in next line 1249 | ⍝ test failed! Execution suspended so that you can examine the problem... 1250 | :EndIf 1251 | ∇ 1252 | 1253 | ∇ z←A IsNotElement B 1254 | z←~A{a←⍺ ⋄ 1<''⍴⍴,a:∧/(⊂a)∊⍵ ⋄ ∧/a∊⍵}B 1255 | :If z 1256 | :AndIf ##.halt 1257 | ⎕←'A IsNotElement B!' 1258 | :If 200≥⎕SIZE'A' 1259 | ⎕←'A=',,A 1260 | :EndIf 1261 | :If 200≥⎕SIZE'B' 1262 | ⎕←'B=',,B 1263 | :EndIf 1264 | ⎕←(2⊃⎕XSI),'[',(⍕2⊃⎕LC),'] ',(1+2⊃⎕LC)⊃⎕THIS.⎕NR 2⊃⎕XSI 1265 | (1+⊃⎕LC)⎕STOP 1⊃⎕XSI ⍝ stop in next line 1266 | ⍝ test failed! Execution suspended so that you can examine the problem... 1267 | :EndIf 1268 | ∇ 1269 | 1270 | ∇ R←{ctxt}RandomVal arg;rFile;r 1271 | ⍝ generate random values 1272 | :If (,arg)≡,1 1273 | :OrIf arg≡1 1 1274 | R←1 1275 | →0 1276 | :EndIf 1277 | :If 0=⎕NC'ctxt' 1278 | ctxt←⎕SI[2 3]{(1⊃⍺),'_',(1⊃⍵),'_',(2⊃⍺),'_',2⊃⍵}⍕¨⎕LC[2 3] 1279 | :EndIf ⍝ use ⎕SI as indicator of context 1280 | rFile←TESTSOURCE,ctxt,'.rng.txt' ⍝ name of rng file 1281 | :If ⎕NEXISTS rFile ⍝ found one - so reuse those numbers (instead of creating new series) 1282 | r←∊1⊃⎕NGET rFile 1283 | (((⎕UCS r)∊10 13)/r)←' ' 1284 | R←2⊃⎕VFI r 1285 | :Else 1286 | :If 1<≢arg 1287 | R←arg[2]?arg[1] 1288 | :Else 1289 | R←?arg 1290 | :EndIf 1291 | (⍕R)Put rFile ⍝ "remember" the generated numbers 1292 | :EndIf 1293 | ∇ 1294 | 1295 | 1296 | ∇ res←LoadTestSuite suite;setups;lines;i;cmd;params;names;values;tmp;f;args;path 1297 | :If 0=≢1⊃⎕NPARTS suite 1298 | suite←TESTSOURCE,suite 1299 | :ElseIf '.'≡1⊃1⊃⎕NPARTS suite ⍝ deal with relative paths 1300 | :If '.'≡1⊃1⊃⎕NPARTS TESTSOURCE ⍝ if suite and source are relative, ignore suite's relative folder and use SOURCE's... 1301 | suite←∊(1 ⎕NPARTS TESTSOURCE),1↓⎕NPARTS suite 1302 | :Else 1303 | suite←∊1 ⎕NPARTS TESTSOURCE,suite 1304 | :EndIf 1305 | :EndIf ⍝ default path for a suite is the TESTSOURCE folder 1306 | :If ''≡3⊃⎕NPARTS suite 1307 | suite←suite,'.dyalogtest' 1308 | :EndIf ⍝ default extension 1309 | 1310 | :If ⎕NEXISTS suite 1311 | lines←⊃⎕NGET suite 1 1312 | :Else 1313 | args←,⊂'Test suite "',suite,'" not found.' ⋄ res←0,args ⋄ →0 1314 | :EndIf 1315 | lines←dtb¨↓rmcm↑lines 1316 | args←⎕NS'' 1317 | ⎕RL←2 ⍝ CompCheck: ignore ⍝ use O/S rng 1318 | path←1⊃1 ⎕NPARTS suite 1319 | :For i :In ⍳⍴lines 1320 | :If ':'∊i⊃lines ⍝ Ignore blank lines 1321 | :AndIf '⍝'≠1↑i⊃lines 1322 | (cmd params)←':'Split whiteout i⊃lines 1323 | (names values)←↓[1]↑¯2↑¨(⊂⊂''),¨'='Split¨','Split params 1324 | cmd←lc cmd~' ' ⋄ names←lc names 1325 | 1326 | :If (i=1)∧'dyalogtest'≢cmd 1327 | 'First line of file must define DyalogTest version'⎕SIGNAL 11 1328 | :EndIf 1329 | 1330 | :Select cmd 1331 | :Case 'dyalogtest' 1332 | :If dVersion≥_version←GetNumParam'version' '' 1333 | :If verbose 1334 | 0 Log'DyalogTest version ',⍕_version 1335 | Log'Processing Test Suite "',suite,'"' 1336 | :EndIf 1337 | :Else 1338 | ('This version of ]',Ûcmd,' only supports Dyalog Test file format v',(⍕dVersion),' and lower')⎕SIGNAL 2 1339 | :EndIf 1340 | 1341 | :Case 'setup' 1342 | args.setup←GetParam'fn' '' 1343 | args.setup←{1↓¯1↓⍵/⍨~' '⍷⍵}' ',args.setup,' ' 1344 | 1345 | :Case 'test' 1346 | :If 0=⎕NC'args.tests' 1347 | args.tests←⍬ 1348 | :EndIf 1349 | args.tests,←',',GetParam'fn' '' 1350 | 1351 | :Case 'teardown' 1352 | args.teardown←GetParam'fn' '' ⍝ function is there 1353 | 1354 | :CaseList 'id' 'description' 1355 | :If verbose 1356 | Log cmd,': ',GetParam'' 1357 | :EndIf 1358 | :Case 'order' 1359 | args.order←2⊃⎕VFI params 1360 | :Case 'codecoverage_subject' ⍝ ,-separated list of namespaces to watch while running test (must be present in ws after setup) 1361 | args.coverage_subj←params 1362 | :Case 'codecoverage_ignore' 1363 | args.coverage_ignore←params 1364 | :Case 'alertifcoveragebelow' 1365 | args.alertifcoveragebelow←2⊃⎕VFI params 1366 | :Case 'successvalue' 1367 | args.SuccessValue←params 1368 | ⍝ ⎕←'args.SuccessValue was set to ',params 1369 | :Else 1370 | Log'Invalid keyword: "',cmd,'"' 1371 | :EndSelect 1372 | :EndIf 1373 | :EndFor 1374 | 1375 | args.tests↓⍨←1 ⍝ drop off leading comma 1376 | res←1,args 1377 | ∇ 1378 | 1379 | :EndSection 1380 | 1381 | :Section BUILD 1382 | 1383 | ∇ {r}←Build args;file;prod;path;lines;extn;name;exists;extension;i;cmd;params;values;names;_description;_id;_version;id;v;target;source;wild;options;z;tmp;types;start;_defaults;f;files;n;quiet;save;ts;tmpPath;chars;nums;fileType;targetNames;targetName;fileContent;fileData;tmpExt;eol;halt;off;LOGS;logfile;TestClassic;production;ClassicVersion;j;synt;rfs;nam;str;wsid;command;line;TargetList;d;order;NQed;type;pars;det;loaded;Target;nosource;lib;icon;rc;oFFIssue;lst 1384 | ⍝ Process a .dyalogbuild file 1385 | Init 2 1386 | oFFIssue←0 ⍝ set to 1 to repo MB's Keypress issue... 1387 | rc←0 ⍝ returncode (if possible) 0=ok, 1=errors during Build process 1388 | LOGS←3⍴⊂'' 1389 | r←'' 1390 | :If isChar args ⍝ also allow the function to be called directly (not as a UCMD) with a simple string arg that we will then parse using DBuilds Parse rules: 1391 | lst←List 1392 | lst←lst[lst.Name⍳⊂'DBuild'] 1393 | i←lst.Parse⍳' ' 1394 | synt←(i↓lst.Parse)('nargs=',i↑lst.Parse) 1395 | args←(⎕NEW ⎕SE.Parser synt).Parse args 1396 | :If 19>DyaVersion 1397 | args.nosource←0 ⍝ avoid VALUE ERROR (Parse only allow for nosource from 19 onwards...) 1398 | :EndIf 1399 | args.(quiet save production halt nosource)←{2⊃⎕VFI⍕⍵}¨args.(quiet save production halt nosource) ⍝ saw a string here when we went through the Parsing above - so let's ensure these vars are numeric... 1400 | :EndIf 1401 | start←⎕AI[3] 1402 | extension←'.dyalogbuild' ⍝ default extension 1403 | i←0 ⍝ we are on "line zero" if any logging happens 1404 | 1405 | :If 0∊⍴args.Arguments 1406 | args.Arguments←,⊂file←FindBuildFile ⎕WSID 1407 | args.clear←1 ⍝ Rebuilding workspace 1408 | :AndIf 0∊⍴file 1409 | 'Build file not named and no default found'⎕SIGNAL 22 1410 | :EndIf 1411 | 1412 | file←∊1 ⎕NPARTS 1⊃args.Arguments 1413 | :If args.production ⍝ #11: if prod is set, quiet←1 and save←0 (unless set differently on the commandline) 1414 | :If 0≡args.quiet 1415 | args.quiet←1 1416 | :EndIf 1417 | :If 0≡args.save 1418 | args.save←0 1419 | :EndIf 1420 | :EndIf 1421 | (prod quiet save halt TestClassic Target)←args.(production quiet save halt testclassic target) 1422 | 1423 | (TestClassic prod save)←{2⊃⎕VFI⍕⍵}¨TestClassic prod save ⍝ these get passed as char (but could also be numeric in case we're being called directly. So better be paranoid and ensure that we have a number) 1424 | off←2 args.Switch'off' 1425 | nosource←¯1 args.Switch'nosource' ⍝ ¯1 indicates "not set" 1426 | 1427 | :If Target≡null 1428 | TargetList←0 5⍴'' ⍝ List of Targets we have to build ([;1]=lineno, [;2]=params names values) 1429 | :Else 1430 | TargetList←1 5⍴0('target: ',Target)('wsid=',Target)(,⊂'wsid')(,⊂Target) 1431 | :EndIf 1432 | 1433 | :If halt 1434 | ⎕TRAP←0 'S' 1435 | :EndIf ⍝ Defeat UCMD trapping 1436 | 1437 | Clear args.clear 1438 | (exists file)←OpenFile file 1439 | (path name extn)←⎕NPARTS file 1440 | 1441 | 0 Log'DyalogBuild version ',⍕dVersion 1442 | ('Build file not found: ',file)⎕SIGNAL exists↓22 1443 | 1444 | lines←1⊃⎕NGET file 1 1445 | 1446 | _version←0 1447 | _id←'' 1448 | _description←'' 1449 | _defaults←'⎕ML←⎕IO←1' 1450 | :If ~prod 1451 | ('Type' 'I')Log'Note: Loaded files will be linked to their source - use -prod to not link' 1452 | :EndIf 1453 | :For i :In ⍳≢lines 1454 | :If ~':'∊line←i⊃lines ⍝ if the line does not have a name value setting 1455 | :OrIf '⍝'=⊃{(⍵≠' ')/⍵}line ⍝ or if it's a comment 1456 | :Continue ⍝ skip it! 1457 | :EndIf ⍝ Ignore blank lines 1458 | line←{(∧\(~2|+\⍵='''')⍲⍵='⍝')/⍵}line 1459 | (cmd params)←':'SplitFirst whiteout line 1460 | params←⎕SE.Dyalog.Utils.ExpandConfig params 1461 | (names values)←↓[1]↑¯2↑¨(⊂⊂''),¨'='sSplit¨','sSplit params 1462 | cmd←lc cmd~' ' ⋄ names←lc names 1463 | params←unq params 1464 | :If (i=1)∧'dyalogbuild'≢cmd 1465 | 'First line of file must define DyalogBuild version'⎕SIGNAL 11 1466 | :EndIf 1467 | 1468 | :Select cmd 1469 | :Case 'dyalogbuild' 1470 | :If dVersion≥_version←GetNumParam'version' '' 1471 | 0 Log'Processing "',file,'" (written for version ≥ ',(⍕_version),')' 1472 | :Else 1473 | ('Type' 'E')Log('This version of ]',Ûcmd,' only supports Dyalog Test file format v',(⍕dVersion),' and lower') 1474 | :EndIf 1475 | 1476 | :Case 'id' 1477 | id←GetParam'id' '' 1478 | v←GetNumParam'version' 1479 | Log'Building ',id,(v≠0)/' version ',⍕v 1480 | 1481 | :Case 'description' 1482 | ⍝ no action 1483 | 1484 | :Case 'copy' 1485 | wild←'*'∊source←GetParam'file' '' 1486 | target←GetParam'target' 1487 | 1488 | :If ⎕NEXISTS path,target 1489 | :For f :In files←'*'ListFiles path,target 1490 | ⎕NDELETE f 1491 | :EndFor 1492 | :Else 1493 | :Trap 0 1494 | 3 ⎕MKDIR path,target ⍝ /// needs error trapping 1495 | :Else 1496 | LogError'Error while creating "',path,target,'":',∊⎕DMX.DM,¨⊂NL 1497 | :EndTrap 1498 | :EndIf 1499 | 1500 | :If 0∊⍴files←source ListFiles path 1501 | LogError'No files found to copy in ":',path,source,'"' 1502 | :Else 1503 | :For f :In files 1504 | cmd←((1+WIN)⊃'cp' 'copy'),' "',f,'" "',path,target,'/"' 1505 | ((WIN∧cmd∊'/')/cmd)←'\' 1506 | {}⎕CMD cmd 1507 | :EndFor 1508 | :EndIf 1509 | :If (n←⍴files)≠tmp←⍴'*'ListFiles path,target,'/' 1510 | LogError(⍕n),' expected, but ',(⍕tmp),' files ended up in "',target,'"' 1511 | :Else 1512 | Log(⍕n),' file',((n≠1)/'s'),' copied from "',source,'" to "',target,'"' 1513 | :EndIf 1514 | 1515 | :Case 'run' 1516 | LogError'run is under development' 1517 | :Continue 1518 | 1519 | tmp←GetParam'file' '' 1520 | (exists tmp)←OpenFile tmp 1521 | :If ~exists 1522 | LogError'unable to find file ',tmp ⋄ :Continue 1523 | :EndIf 1524 | 1525 | :CaseList 'ns' 'class' 'csv' 'apl' 'lib' 'data' 1526 | d←0 ⍝ will be set to 1 if we come across errors handling assignments 1527 | target←'#'GetParam'target' 1528 | target←(('#'≠⊃target)/'#.'),target 1529 | :If 0∊⍴source←GetParam'source' '' 1530 | 'Source is required'Signal 11 1531 | :EndIf 1532 | :If cmd≡'ns' 1533 | :If 0=⎕NC target 1534 | target ⎕NS'' 1535 | :Trap halt↓0 1536 | target⍎_defaults 1537 | Log'Created namespace ',target 1538 | :Else 1539 | LogError'Error establishing defaults in namespace ',target,': ',⎕JSON ⎕DMX ⍝ CompCheck: ignore 1540 | :EndTrap 1541 | :ElseIf 2=⎕NC target ⍝ if target is an existing variable name 1542 | LogError'Can not create namespace ',target,' - a variable with that name already exists' 1543 | :EndIf 1544 | :EndIf 1545 | 1546 | :If cmd≡'csv' 1547 | types←2⊃⎕VFI GetParam'coltypes' 1548 | :If ~0=tmp←#.⎕NC target 1549 | LogError'Not a free variable name: ',target,', current name class = ',⍕tmp ⋄ :Continue 1550 | :EndIf 1551 | :Trap halt↓999 1552 | tmp←∆CSV(path,source)'',(0≠⍴types)/⊂types 1553 | ⍎target,'←tmp' 1554 | Log target,' defined from CSV file "',source,'"' 1555 | :Else 1556 | LogError ⎕DMX.(OSError{⍵,2⌽(×≢⊃⍬⍴2⌽⍺)/'") ("',⊃⍬⍴2⌽⍺}Message{⍵,⍺,⍨': '/⍨×≢⍺}⊃⍬⍴DM,⊂'') ⍝ CompCheck: ignore 1557 | :EndTrap 1558 | :Continue 1559 | :EndIf 1560 | 1561 | wild←∨/'*?'∊source 1562 | options←(wild/' -protect'),(prod/' -nolink'),(' -source'/⍨cmd≡'data') ⍝ protect started with Dyalog 14 (or was it 13?) 1563 | tmpPath←path{cmd≡'lib':⍵ ⋄ ⍵,⍨⍺/⍨0∊⍴('^\[.*\]'⎕S 3)⍵}source ⍝ CompCheck: ignore 1564 | :If cmd≡'lib' ⍝ find path of library...(only if >17, so we'll be using ]LINK which needs path) 1565 | lib←⊃0(⎕NINFO ⎕OPT('Wildcard' 1)('Recurse' 1))((2 ⎕NQ'.' 'GetEnvironment' 'DYALOG'),'/Library/',source,'.dyalog') ⍝ CompCheck: ignore 1566 | lib←eis lib 1567 | :If 1=≢lib ⍝ CompCheck: ignore 1568 | tmpPath←⊃lib 1569 | :ElseIf 1<≢lib ⍝ CompCheck: ignore 1570 | LogError'too many matches searching library "',source,'": ',⍕lib 1571 | :Continue 1572 | :Else 1573 | LogError'Could not find library "',source,'"' 1574 | :Continue 1575 | :EndIf 1576 | :EndIf 1577 | :Trap halt↓11 1578 | ⍝z←⎕SE.SALT.Load tmp←tmpPath,((~0∊⍴target)/' -target=',target),options 1579 | loaded←options LoadCode tmpPath target cmd 1580 | ⍝ (2⊃¨loaded)←{(,⊂⍣(2=≡⍵)rtack ⍵)}¨2⊃¨loaded 1581 | :Else 1582 | LogError ⎕DMX.(OSError{⍵,2⌽(×≢⊃⍬⍴2⌽⍺)/'") ("',⊃⍬⍴2⌽⍺}Message{⍵,⍺,⍨': '/⍨×≢⍺}⊃⍬⍴DM,⊂'') ⍝ CompCheck: ignore 1583 | :Continue 1584 | :EndTrap 1585 | 1586 | :If cmd≡'data' 1587 | target ⎕NS'' 1588 | fileType←lc'charvecs'GetParam'format' 1589 | :If 'charvec'≡fileType 1590 | chars←'cr' 'lf' 'nel' 'vt' 'ff' 'ls' 'ps' 1591 | nums←'13' '10' '133' '11' '12' '8232' '8233' 1592 | eol←⎕UCS⍎¨(chars,nums)⎕S(,⍨nums)rtack lc'lf'GetParam'seteol' ⍝ CompCheck: ignore 1593 | :EndIf 1594 | tmpExt←3⊃⎕NPARTS tmpPath 1595 | tmpExt,⍨←'='/⍨0≠⍴tmpExt 1596 | :If wild 1597 | targetNames←2⊃¨⎕NPARTS,eis(⎕SE.SALT.List tmpPath,' -extension',tmpExt,' -raw')[;2] 1598 | :ElseIf 1=≢loaded 1599 | targetNames←2⊃⎕NPARTS tmpPath 1600 | :ElseIf 0=≢loaded 1601 | LogError'LoadCode "',tmpPath,'" did not return anything - does the file even exist?' 1602 | :Continue 1603 | :Else 1604 | LogError'"LoadCode" unexpectedly returned > 1 object for command "',line,'"' 1605 | :Continue 1606 | :EndIf 1607 | :For targetName fileContent :In loaded 1608 | targetName←2⊃⎕NPARTS targetName 1609 | ⍝ fileContent←⊃fileContent 1610 | :Select fileType 1611 | :Case 'charvec' 1612 | fileData←(-≢eol)↓∊fileContent,¨⊂eol 1613 | :Case 'charmat' 1614 | fileData←↑fileContent 1615 | :Case 'json' 1616 | fileData←0 ⎕JSON∊fileContent 1617 | :Case 'charvecs' 1618 | fileData←fileContent 1619 | :EndSelect 1620 | :Trap 0 1621 | :If 0=(⍎target).⎕NC targetName 1622 | targetName(⍎target).{⍎⍺,'←⍵'}fileData 1623 | :Else 1624 | LogError'DATA does not support overwriting of existing names (as command "',(i⊃lines),'" would do)' 1625 | d←1 1626 | :Continue 1627 | :EndIf 1628 | :Else 1629 | LogError'Error trying to assign "',target,'.',targetName,'": ',NL,∊⎕DMX.DM,¨⊂NL 1630 | d←1 1631 | :Continue 1632 | :EndTrap 1633 | :EndFor 1634 | loaded←targetNames 1635 | fileType,←' ' 1636 | :Else 1637 | fileType←'' 1638 | :EndIf 1639 | :If ~d 1640 | :If 0∊⍴loaded ⍝ no names 1641 | LogError'Nothing found: ',source 1642 | :ElseIf (,1)≡,⍴loaded ⍝ exactly one name 1643 | Log{(uc 1↑⍵),1↓⍵}fileType,cmd,' ',source,' loaded as ',⍕⊃loaded 1644 | :Else ⍝ many names: -verbose shows complete list always, otherwise limit to ⎕PW 1645 | Log((⍕⍴,loaded),' ',fileType,' names loaded from ',source,' into ',(⍕target),'.'){⎕PW>12+≢⍺,⍵:⍺,⍵ ⋄ ⍺}{1=≡⍵:⍵ ⋄ '(',(¯1↓∊⍕¨⍵,¨' '),')'}loaded 1646 | :EndIf 1647 | :EndIf 1648 | 1649 | 1650 | :CaseList 'lx' 'exec' 'prod' 'defaults' 1651 | :If 0∊⍴tmp←GetParam'expression' '' 1652 | LogError'expression missing' 1653 | LogError'expression missing in line >',line,'<' 1654 | :Else 1655 | tmp←params ⍝ MBaas: use entire segment after ":" as argument (so that : and , can be used in these APL Expressions!) 1656 | :If cmd≡'lx' 1657 | #.⎕LX←tmp 1658 | Log'Latent Expression set' 1659 | :ElseIf prod∨cmd≢'prod' ⍝ only execute PROD command if -production specified 1660 | :Trap halt↓0 1661 | #⍎tmp 1662 | :Else 1663 | LogError,∊⎕DMX.DM,¨⊂NL 1664 | :EndTrap 1665 | :If cmd≡'defaults' 1666 | _defaults←_defaults,'⋄',tmp ⋄ Log'Set defaults ',tmp 1667 | #⍎_defaults ⍝ apply defaults to # 1668 | :EndIf ⍝ Store for use each time a NS is created 1669 | :EndIf 1670 | :EndIf 1671 | 1672 | :Case 'target' 1673 | :If (,0)≡2 args.Switch'save' 1674 | :AndIf (('2'GetNumParam'save')∊0 1) 1675 | ('Type' 'I')Log'Found TARGET-Entry with SAVE-parameter, but modifier -save=',(⍕save),' overruled it' 1676 | :ElseIf Target≡null 1677 | TargetList⍪←i line params names values 1678 | :EndIf 1679 | :Else 1680 | :If '⍝'≠⊃cmd ⍝ ignore commented lines 1681 | :AndIf 0<≢cmd 1682 | LogError'Invalid keyword: ',cmd 1683 | :EndIf 1684 | :EndSelect 1685 | 1686 | :EndFor 1687 | 1688 | :If prod 1689 | ⎕EX'#.SALT_Var_Data' 1690 | :EndIf 1691 | ⎕EX'loaded' ⍝ kill possible refs (otherwise ⎕save may fail) 1692 | :If TestClassic>0 1693 | z←TestClassic{ 1694 | 2=⎕NC ⍵:⍵{0<⍴,⍵:⍺,': ',⍵ ⋄ ''}∆TestClassic⍎⍵ 1695 | 3=⎕NC ⍵:⍵{0<⍴,⍵:⍺,': ',⍵ ⋄ ''}∆TestClassic ⎕CR ⍵ 1696 | +∇¨(⊂⍵,'.'),¨(⍎⍵).⎕NL ¯2.1 ¯3.1 ¯9.1 ⍝ +∇ avoids crashes in 12.1...15 1697 | }¨(⊂'#.'),¨#.⎕NL ¯2.1 ¯3.1 ¯3.2 ¯9.1 1698 | :If 0<⍴z 1699 | LogError('Classic test found incompatible characters in following functions/variables:',NL),¯2↓∊z{('- ',⍺,⍵)/⍨×,⍴⍺}⍥1 rtack NL 1700 | :Else 1701 | Log'Workspace seems to be compatible with Classic Edition ',⍕{⍵>1:⍵ ⋄ 12}TestClassic 1702 | :EndIf 1703 | :EndIf 1704 | 1705 | n←≢3⊃LOGS 1706 | :If DyaVersion≥19 1707 | :If nosource>¯1 ⍝ if this is set 1708 | :AndIf 0<≢GetParam'nosource' ⍝ and the TARGET instruction also has a nosource param 1709 | :AndIf ('2'GetNumParam'nosource')≠nosource ⍝ and they are different 1710 | ('Type' 'W')Log'Found TARGET-Entry with nosource=',(GetParam'nosource'),', but modifier -nosource=',(⍕nosource),' overruled it' 1711 | :EndIf 1712 | :If (1=GetNumParam'nosource')∧0≠2 args.Switch'nosource' 1713 | :OrIf 1=2 args.Switch'nosource' 1714 | {}5171⌶# 1715 | {}5172⌶1 1716 | :EndIf 1717 | :Else 1718 | :If 0<≢GetParam'nosource' 1719 | :OrIf nosource>¯1 1720 | ('Type' 'W')Log'Use of "nosource" requires Dyalog version 19.0 or later' 1721 | :EndIf 1722 | :EndIf 1723 | :If 0=n ⍝ if no errors were found 1724 | :If (save≡1)∧0=1↑⍴TargetList ⍝ save switch was set, but no target instruction given 1725 | ⍝ pretend we had one which save under name of build file 1726 | TargetList←1 5⍴0('target: ',name)('wsid=',name)(,⊂'wsid')(,⊂name) 1727 | :EndIf 1728 | :If save 1729 | :For (i line params names values) :In ↓TargetList 1730 | :If 0∊⍴tmp←GetParam'wsid' '' 1731 | LogError'wsid missing' 1732 | :Else 1733 | d←1⊃⎕NPARTS tmp ⍝ directory given? 1734 | :If {{~'/\'∊⍨(⎕IO+2×isWin∧':'∊⍵)⊃⍵}3↑⍵}d ⍝ if that dir is an relative path 1735 | wsid←∊1 ⎕NPARTS path,tmp ⍝ prefix path of buildfile 1736 | :Else 1737 | wsid←tmp 1738 | :EndIf 1739 | :If ~⎕NEXISTS 1⊃⎕NPARTS wsid 1740 | LogError'Folder of wsid ("',(1⊃⎕NPARTS wsid),'") not found! wsid will not be set and ws not saved!' 1741 | :Continue 1742 | :EndIf 1743 | :If (⊂lc 3⊃⎕NPARTS wsid)∊'' '.dws' 1744 | :OrIf 0=≢GetParam'type' ⍝ if type is not set, we're building a workspace 1745 | :If (save∊⍳2)∨99='99'GetNumParam'save' 1746 | ⎕WSID←wsid 1747 | Log'WSID set to ',wsid 1748 | :EndIf 1749 | :EndIf 1750 | :EndIf 1751 | 1752 | :If off=2 1753 | off←1=GetNumParam'off' 0 1754 | :EndIf ⍝ only process this one if the modifier was not provided (and therefore has its default value of 2) 1755 | :If save∊0 2 1756 | :Continue 1757 | :EndIf 1758 | 1759 | ⍝ Apr 21-research found these vars referencing # (or elements of it) - get them out of the way temporarily 1760 | rfs←0 2⍴'' 1761 | ⎕EX¨'⎕SE.'∘,¨'SALTUtils.spc.z' 'SALTUtils.spc.res' 1762 | :Trap 0 1763 | :For nam :In '⎕SE.'∘,¨'THIS' 'SALTUtils.cs' 'SALTUtils.c.THIS' 'SALTUtils.spc.ns.proc' 'input.c.THIS' 1764 | :If 0<⎕NC nam 1765 | :AndIf 326=⎕DR⍎nam 1766 | str←⍕⍎nam 1767 | :If 1=⍴⍴⍎nam 1768 | str←',',str 1769 | :EndIf 1770 | :If (⍎nam)≢⍎str ⍝ CompCheck: ignore 1771 | Log'⎕SAVE workaround failed because of ',nam 1772 | :EndIf 1773 | rfs⍪←(nam)(str) ⍝ remember refs stringified... 1774 | ⎕EX nam ⍝ and delete them 1775 | :EndIf 1776 | :EndFor 1777 | :Else 1778 | ('Type' 'W')Log'⎕SAVE workaround failed because of ',nam 1779 | rfs←0 2⍴'' 1780 | :EndTrap 1781 | ⎕SIGNAL 0 ⍝ CompCheck: ignore ⍝ reset ⎕DM, ⎕DMX to avoid problems with refs when saving 1782 | :Trap DEBUG↓0 ⍝ yes, all trap have a halt/ after them - this one doesn't and shouldn't. 1783 | :If ~0∊⍴type←GetParam'type' 1784 | :If DyaVersion≥19 ⍝ Can we save? This feature is available on all platforms from v19.0 and Windows from v13.0, so check if we have the right version... 1785 | :OrIf _isWin∧DyaVersion≥13 1786 | ⍝ is one of 'ActiveXControl' 'InProcessServer' 'Library' 'NativeExe' 'OutOfProcessServer' 'StandaloneNativeExe' 1787 | ⍝ is the sum of zero or more of the following: 1788 | ⍝ BOUND_CONSOLE 2 1789 | ⍝ BOUND_USEDOTNET 4 1790 | ⍝ BOUND_RUNTIME 8 1791 | ⍝ BOUND_XPLOOK 32 1792 | ⍝ 1793 | ⍝ is a filename the contents of which will be inserted as a resource in the bound file (used by ASP.NET) 1794 | ⍝ is the name of an icon file, the contents of which are used as the main icon for the bound file 1795 | ⍝ is the command line that is bound in the find and passed to dyalog.dll when the dll is started 1796 | ⍝
is a 2 column matrix of character vectors which allow the APL programmer to add an RT_VERSION resource to the executable. 1797 | ⍝ This allows the program to populate the Properties the "Details" tab in the Properties dialog which can be opened by right clicking 1798 | ⍝ on an executable in Windows Explorer. The first column is the Property, the second the Value. Note that the properties are not 1799 | ⍝ limited to those displayed in the dialog, nor are any of the names or values validated. Note also that the name of the property 1800 | ⍝ that appears in the dialog is not necessarily the name that the program must use .. 1801 | ⍝ search for "string-name" in https://msdn.microsoft.com/en-us/library/windows/desktop/aa381058(v=vs.85).aspx for more details for executables. 1802 | ⍝ For .NET assemblies, look at https://msdn.microsoft.com/en-us/library/system.reflection(v=vs.110).aspx; 1803 | ⍝ any of the classes listed which has a constructor which takes a single string value as its argument should be definable. 1804 | det←⊃,/':'Split¨';'Split GetParam'details' 1805 | :If 0<≢∊det 1806 | det←(⌽2,0.5×⍴det)⍴det 1807 | :Else 1808 | det←0 2⍴'' 1809 | :EndIf 1810 | icon←GetParam'icon' 1811 | :If (⊂2↑icon)∊'./' '.\' 1812 | icon←path,2↓icon 1813 | :EndIf 1814 | pars←'.' 'Bind'wsid(type)(GetNumParam'flags')(GetParam'resource')(icon)('"'~⍨GetParam'cmdline')(det) 1815 | command←'2 ⎕NQ ',∊{''≡0↑⍵:'''',⍵,''' ' ⋄ (⍕⍵),' '}¨¯1↓pars 1816 | command←command,(0<≢det)/' (',(⍕⍴det),'⍴',(∊{''≡0↑⍵:'''',⍵,''' ' ⋄ (⍕⍵),' '}¨det),')' 1817 | 2 #.⎕NQ pars 1818 | :Else 1819 | :If ~_isWin 1820 | ('Type' 'E')Log'Use of the "type" parameter with "TARGET" requires Dyalog version 19.0 or later' 1821 | :Else 1822 | ('Type' 'E')Log'Use of the "type" parameter with "TARGET" requires Dyalog version 13.0 or later' 1823 | :EndIf 1824 | :EndIf 1825 | :Else 1826 | command←')SAVE ',wsid 1827 | 0 #.⎕SAVE wsid 1828 | :EndIf 1829 | :Trap DEBUG↓0 ⍝ paranoid, but want to avoid any bugs here to trigger the save again... 1830 | :If ⎕NEXISTS det←wsid{''≡3⊃⎕NPARTS ⍺:⍺,⍵ ⋄ ⍺}'.dws' 1831 | tmp←⍕DEBUG{(~⍺)/~⍺::'???' ⋄ (ListFiles ⍵)[1;2]}det 1832 | Log'Saved as ',det,' (',tmp,' bytes)' 1833 | :EndIf 1834 | :EndTrap 1835 | command←'' 1836 | :Case 11 ⍝ DOMAIN ERROR 1837 | :If 0<102⌶# ⍝ check most likely cause: links from ⎕SE to # 1838 | :AndIf isWin 1839 | ('Type' 'E')Log'Problem creating ',wsid,':',NL,(∊⎕DMX.DM,¨⊂NL),'There might still be references from "somewhere in ⎕SE" to "something in #".',NL,'Please contact support@dyalog.com to discuss & resolve this if the enqueued keystrokes did not create the desired result.',NL,command,' ⍝ command we executed',NL 1840 | :Else 1841 | ('Type' 'E')Log'Problem creating ',wsid,':',NL,∊⎕DMX.DM,¨⊂NL 1842 | :EndIf 1843 | :If halt ⋄ (⎕LC[1]+2)⎕STOP 1⊃⎕XSI 1844 | ⎕←'Function halted.' 1845 | ⍝ stop here 1846 | :EndIf 1847 | :Else 1848 | ('Type' 'E')Log'Problem creating ',wsid,':',∊⎕DMX.DM,¨⊂NL 1849 | :EndTrap 1850 | :If ~0∊⍴command 1851 | :If ⎕NEXISTS wsid,'.dws' 1852 | :AndIf ~'.exe'≡3⊃⎕NPARTS wsid 1853 | ⎕NDELETE wsid,'.dws' ⍝ avoid prompts during )SAVE 1854 | :EndIf 1855 | :If isWin 1856 | {sink←2 ⎕NQ ⎕SE'keypress'⍵}¨' )RESET',⊂'ER' 1857 | {sink←2 ⎕NQ ⎕SE'keypress'⍵}¨' ',command,⊂'ER' 1858 | NQed←1 1859 | Log'Enqueued keypresses to automatically save after UCMD has completed: "',command,'"' 1860 | :Else 1861 | Log'Please execute the following command when the UCMD has finished:' 1862 | Log command 1863 | :EndIf 1864 | :EndIf 1865 | :If 0<≢rfs ⍝ and created some refs 1866 | :For (nam str) :In ↓rfs ⍝ then restore them... 1867 | ⍎nam,'←',str 1868 | :EndFor 1869 | :EndIf 1870 | :EndFor 1871 | :ElseIf 0<≢TargetList 1872 | ('Type' 'W')Log'TARGETs were not saved because -save Switch was not set!' 1873 | :EndIf 1874 | :Else 1875 | ('Type' 'W')Log'DBuild found errors during process',save/', workspace was not saved!' 1876 | n←1 ⍝ need error count 1877 | rc←1 1878 | :EndIf 1879 | ⍝:EndIf 1880 | ⍝ :endif 1881 | endSave: 1882 | ('Type' 'I')Log'DyalogBuild: ',(⍕⍴lines),' lines processed in ',(1⍕0.001×⎕AI[3]-start),' seconds.' 1883 | 1884 | :If 01)/'s'),' encountered.' 1886 | :EndIf 1887 | :For i :In ⍳3 1888 | :If 01)/⍕n),' ',i⊃'Info' 'Warning' 'Error'),((n>1)/'s'),':' 1890 | r,←i⊃LOGS 1891 | :EndIf 1892 | :EndFor 1893 | r←table r 1894 | :If off=1 ⍝ careful: off∊0 1 2! 1895 | logfile←∊(2↑⎕NPARTS file),'.log' 1896 | 1 ⎕NDELETE logfile 1897 | :If ~0∊⍴3⊃LOGS 1898 | (∊r,¨⊂NL)Put logfile 1899 | :EndIf 1900 | :If isWin 1901 | ⍝ June 2021, m19713: need to leading blanks to avoid an issue with 171c64, 180c32, 180c64, 181c32, 181c64 where ')OFF' would result in "oFF". 1902 | ⍝ (No simple repro and not a problem on (121c64, 140c64, 141c64, 150c32) 1903 | :If 0=⎕NC'NQed' 1904 | :OrIf ~NQed 1905 | ⎕OFF rc 1906 | :Else 1907 | {sink←2 ⎕NQ ⎕SE'keypress'⍵}¨((~oFFIssue)/' '),')OFF',⊂'ER' ⍝ as long as 18008 isn't fixed (and for all older versions) we can't use ⎕OFF but have to ⎕NQ'KeyPress' 1908 | →0 1909 | :EndIf 1910 | :Else 1911 | :If 0=n ⍝ if we found no errors 1912 | ⎕OFF ⍝ it is save to exit 1913 | :Else ⍝ otherwise 1914 | ⎕←' )OFF ⍝ errors in DBuild prevent automic OFFing...' ⍝ tell the user 1915 | :EndIf 1916 | :EndIf 1917 | :ElseIf 2=⎕SE.⎕NC'DBuild_postSave' 1918 | ⍎⎕SE.DBuild_postSave 1919 | :EndIf ⍝ we exit with 1 if there were errors, 0 if everything's fine. 1920 | ∇ 1921 | 1922 | ∇ r←FindBuildFile path;found;file;ext 1923 | r←'' 1924 | :Repeat 1925 | path←(-(¯1↑path)∊'/\')↓path ⍝ drop trailing / or \ 1926 | (path file ext)←⎕NPARTS path 1927 | :Until found←⎕NEXISTS r←path,file,'/',file,'.dyalogbuild' 1928 | :OrIf 1≥+/r∊'/\' 1929 | r←found/r 1930 | ∇ 1931 | 1932 | ∇ (exists file)←OpenFile file;tmp;path;extn;name 1933 | (path name extn)←⎕NPARTS file 1934 | :If exists←⎕NEXISTS file 1935 | :If 1=GetFilesystemType file ⍝ but it is a folder! 1936 | :If exists←⎕NEXISTS tmp←file,'/',name,extension ⍝ If folder contains name.dyalogbuild 1937 | file←tmp ⍝ Then use the file instead 1938 | :ElseIf 1=⍴tmp←(ListFiles(⊃1 ⎕NPARTS file),'*',extension)[;1] ⍝ if there's only a single .dyalogbuild file, use it 1939 | exists←⎕NEXISTS file←⊃tmp 1940 | :ElseIf 1<⍴tmp 1941 | LogError'There is more than one ',(extension),' file in ',file,'. Please specify a single file.' 1942 | :EndIf 1943 | :EndIf 1944 | :Else 1945 | exists←⎕NEXISTS file←file,(0∊⍴extn)/extension 1946 | :EndIf 1947 | ∇ 1948 | 1949 | ∇ Clear clear;tmp;n 1950 | →(clear≡0)⍴0 1951 | :If (clear≡1)∨0∊⍴,clear 1952 | ⍝ #.(⎕EX ⎕NL⍳9) 1953 | #.⎕EX #.⎕NL⍳9 1954 | {}'(all:1)'⎕SE.Link.Break # 1955 | Log'workspace cleared' 1956 | :ElseIf ∧/1⊃tmp←⎕VFI clear 1957 | n←#.⎕NL 2⊃tmp 1958 | #.⎕EX n ⋄ Log'Expunged ',(⍕⍴n),' names of class ',clear 1959 | :Else 1960 | LogError'invalid argument to clear, should be empty or a numeric list of name classes to expunge' 1961 | :EndIf 1962 | ∇ 1963 | 1964 | LineNo←{ '[',(,'ZI3'⎕FMT ⊃,⍵),']' } ⍝ m19572 deals with Edit|Reformat not removing the blanks in the dfn! 1965 | PrefixTS← {(⊃'hh:mm:ss.fff"> "'(1200⌶)1⎕DT'J'),⍵} 1966 | 1967 | ∇ {r}←{f}LogTest msg;type;i 1968 | ⍝ this function is mapped to function "Log" that is defined in the ns in which tests are executed 1969 | ⍝ optional f is ('Type' 'I|W|E') (or 'Info|Warning|Error', 1st char matters) and/or ('Prefix' 'any text to prefix to the msg') 1970 | ⍝ msg is the ReturnValue of a test, traditionally we expect a text vector there, but anything that matches "SuccessValue" (empty string) 1971 | ⍝ will not be logged whereas anything different will be logged as an error, unless specified differently through 'Type'. 1972 | r←0 0⍴0 ⋄ type←0 ⍝ initial value... 1973 | 1974 | →(msg≡SuccessValue)⍴0 1975 | :If 0=⎕NC'f' 1976 | :AndIf (⎕DR∊msg)∊80 82 160 1977 | f←'' 1978 | r←msg 1979 | type←3 1980 | :Else 1981 | :If 2≤|≡f 1982 | :If 2=|≡f ⍝ ONE name & value 1983 | f←⊂f 1984 | :EndIf 1985 | f←,f 1986 | :If (≢f)≥i←(,1↑¨f)⍳⊂,⊂'Type' 1987 | type←'IWE'⍳⊃2⊃i⊃f 1988 | :EndIf 1989 | :If (≢f)≥i←(,1↑¨f)⍳⊂,⊂'Prefix' 1990 | f←2⊃i⊃f 1991 | :Else 1992 | f←'' 1993 | :EndIf 1994 | :EndIf 1995 | f←' ',f ⍝ add some indent for multiline output 1996 | :If type=0 ⍝ only add this information if Log came w/o explicit type 1997 | type←3 ⍝ and the default message type is "Error" 1998 | :If (⎕DR msg)=326 1999 | ⎕←'code returned data with unsupported ⎕DR=326' ⍝ msg←! 2000 | ⎕TRAP←0 'S' ⋄ (⎕LC[1]+1)⎕STOP 1⊃⎕XSI 2001 | :ElseIf ~(⎕DR∊msg)∊80 82 160 2002 | msg←'code returned numeric ',((0 1⍳⍴⍴msg)⊃'scalar' 'vector'),' = ',⍕msg 2003 | :If SuccessValue≢'' 2004 | msg,←' that did not match SuccessValue=',{' '=⍥⎕DR ⍵:'''',⍵,'''' ⋄ ((0 1⍳⍴⍴⍵)⊃'scalar ' 'vector '),⍕⍵}SuccessValue 2005 | :Else 2006 | msg,←' when DTest expected an empty charvec to indicate success' 2007 | :EndIf 2008 | :Else 2009 | msg←'code returned character value = "',((50<≢msg)/(⎕UCS 10),' '),('\n'⎕R'\n '⍠('Mode' 'D'))({(¯2↓msg),(¯2↑msg)~⎕UCS 10 13}msg),'" ' ⍝ add some indent for multiline msgs 2010 | msg,←⎕UCS 10 2011 | :If SuccessValue≢'' 2012 | msg,←'that did not match SuccessValue=',{' '=⍥⎕DR ⍵:'''',⍵,'''' ⋄ 'num ',((0 1⍳⍴⍴⍵)⊃'scalar ' 'vector '),⍕⍵}SuccessValue 2013 | :Else 2014 | msg,←'when DTest expected an empty charvec to indicate success' 2015 | :EndIf 2016 | :EndIf 2017 | :EndIf 2018 | :If 2=⎕NC'timestamp' 2019 | :AndIf timestamp=1 2020 | f←PrefixTS f 2021 | :EndIf 2022 | msg←(f,(~0∊⍴f)/': ')∘,¨eis msg 2023 | 2024 | :EndIf 2025 | :If verbose 2026 | :AndIf quiet=0 2027 | ⎕←msg 2028 | :EndIf 2029 | :If quiet≠1 2030 | :OrIf type=3 2031 | LOGS[type],←⊂eis msg 2032 | :EndIf 2033 | ∇ 2034 | 2035 | ∇ {pre}Log msg;type;j 2036 | ⍝ no ⍺ or ⍺=1: prefix log with lineno. 2037 | ⍝ alternatively pre can also be a VTV with Name/Value pairs ('Prefix' 'foo')('Type' 'I') 2038 | type←1 ⍝ Info 2039 | →(0=≢msg)/0 2040 | :If 0=⎕NC'pre' 2041 | :OrIf pre≡1 2042 | pre←⊂'Prefix'(LineNo i) 2043 | :EndIf 2044 | :If 2=⎕NC'pre' 2045 | :If ~(≡pre)∊0 1 ⍝ if is not a scalar or a simple vec 2046 | :If 2=|≡pre ⍝ ONE name & value 2047 | pre←⊂pre 2048 | :EndIf 2049 | pre←,pre 2050 | :If (≢pre)≥j←(,1↑¨pre)⍳⊂,⊂'Type' 2051 | type←'IWE'⍳⊃2⊃j⊃pre 2052 | :EndIf 2053 | :If (≢pre)≥j←(,1↑¨pre)⍳⊂,⊂'Prefix' 2054 | pre←2⊃j⊃pre 2055 | :Else 2056 | pre←'' 2057 | :EndIf 2058 | :Else 2059 | pre←'' 2060 | :EndIf 2061 | :EndIf 2062 | :If 2=⎕NC'timestamp' 2063 | :AndIf timestamp=1 2064 | pre←PrefixTS pre 2065 | :EndIf 2066 | pre←pre,(∧/(0<≢pre),' '≠(¯1↑pre),1⊃msg)⍴' ' ⍝ optionally insert a blank to separate prefix and msg 2067 | :If 0=⎕NC'LOGS' 2068 | LOGS←3⍴⊂'' 2069 | :EndIf ⍝ may happen during Clean... 2070 | :If quiet≠1 2071 | :OrIf type=3 2072 | LOGS[type],←⊂eis pre,msg 2073 | :EndIf 2074 | :If quiet=0 2075 | ⍝⎕←pre,,msg 2076 | :ElseIf quiet=1 2077 | :AndIf type=3 2078 | ⍝⎕←pre,,msg 2079 | :EndIf 2080 | ∇ 2081 | 2082 | ∇ dm Signal en 2083 | ⍝ subroutine of Build: uses globals i and file 2084 | (dm,' in line ',(LineNo i),' of file ',file)⎕SIGNAL 2 2085 | ∇ 2086 | 2087 | ∇ {r}←{decor}LogError msg 2088 | ⍝ subroutine of Build: uses globals i 2089 | :If 0=⎕NC'decor' 2090 | decor←'*****' 2091 | :EndIf 2092 | decor←decor,' ERROR ',decor 2093 | ('Type' 'E')('Prefix'decor)Log msg 2094 | :If quiet=1 ⍝ make sure that errors are shown (unless we are explicitely told to shut up (quiet=2)- this is mostly relevant when Build is called during ]DTest) 2095 | ⍝⎕←msg 2096 | :EndIf 2097 | 2098 | ∇ 2099 | 2100 | :EndSection 2101 | 2102 | :Section UCMD 2103 | 2104 | ∇ r←List 2105 | Init 1 ⍝ make sure _Version is available... 2106 | r←⎕NS¨3⍴⊂'' 2107 | r.Group←⊂'DEVOPS' 2108 | r.Name←'DBuild' 'DTest' 'GetTools4CITA' 2109 | r.Desc←'Run one or more DyalogBuild script files (.dyalogbuild)' 'Run (a selection of) functions named test_* from a namespace, file or directory' 'Load tools to run CITA tests' 2110 | :If 14>1⊃DyaVersion 2111 | r.Parse←'1S -production -quiet[∊]0 1 2 -halt -save[∊]0 1 2 -off[=]0 1 -clear[=] -target= -testclassic' '1 -clear[=] -tests= -testlog[=] -filter= -setup[=] -teardown[=] -suite= -verbose -quiet -halt -loglvl= -trace -ts -timeout= -repeat= -order= -init -off[=]0 1 2 -SuccessValue=' '' 2112 | :ElseIf 19>DyaVersion 2113 | r.Parse←'1S -production -quiet[∊]0 1 2 -halt -save[∊]0 1 2 -off[=]0 1 -clear[=] -target= -testclassic' '999s -clear[=] -tests= -testlog[=] -filter= -setup[=] -teardown[=] -suite= -verbose -quiet -halt -loglvl= -trace -ts -timeout= -repeat= -order= -init -off[=]0 1 2 -coverage[=] -SuccessValue=' '' 2114 | :Else 2115 | r.Parse←'1S -production -quiet[∊]0 1 2 -halt -nosource[∊]0 1 2 -save[∊]0 1 2 -off[=]0 1 -clear[=] -target= -testclassic' '999s -clear[=] -tests= -testlog[=] -filter= -setup[=] -teardown[=] -suite= -verbose -quiet -halt -loglvl= -trace -ts -timeout= -repeat= -order= -init -off[=]0 1 2 -coverage[=] -SuccessValue=' '' 2116 | :EndIf 2117 | ∇ 2118 | 2119 | ∇ Û←Run(Ûcmd Ûargs) 2120 | ⍝ Run a build 2121 | Init 1 2122 | ('UCMD "',Ûcmd,'" requires at least Dyalog v18.0')⎕SIGNAL(DyaVersion<18)/11 2123 | :Select Ûcmd 2124 | :Case 'DBuild' 2125 | Û←Build Ûargs 2126 | :Case 'DTest' 2127 | Û←Test Ûargs 2128 | :Case 'GetTools4CITA' 2129 | Û←GetTools4CITA Ûargs 2130 | :EndSelect 2131 | ∇ 2132 | 2133 | ∇ r←level Help Cmd;d 2134 | Init 1 2135 | :Select Cmd 2136 | :Case 'DBuild' 2137 | r←⊂'Run one or more DyalogBuild script files (.dyalogbuild) | Version ',2⊃SemVer 2138 | r,←⊂' ]',(⊃List.Group),'.',Cmd,' [-clear[=NCs]] [-production] [-quiet[=0|1|2]] [-halt] ',((19≤DyaVersion)/'[-nosource[=0|1]] '),'[-save[=0|1|2]] [-off[=0|1]] [-TestClassic] -target=Target' 2139 | :Select level 2140 | :Case 0 2141 | r,←⊂']',(⊃List.Group),'.',Cmd,' -?? ⍝ for more details about command line and modifiers' 2142 | r,←⊂']',(⊃List.Group),'.',Cmd,' -??? ⍝ for description of the DyalogBuild script format' 2143 | r,←⊂'see https://github.com/Dyalog/DBuildTest/wiki/DBuild for more information' 2144 | :Case 1 2145 | r,←'' 'Argument is:' 2146 | r,←⊂' files name of one or more .dyalogbuild files' 2147 | r,←'' 'Optional modifiers are:' 2148 | r,←⊂' -clear[=NCs] expunge all objects, optionally of specified nameclasses only' 2149 | r,←⊂' -halt halt on error rather than log and continue' 2150 | :If 19≤DyaVersion 2151 | r,←⊂' -nosource do not preserve "source-as-typed" (neccessary if you want to create workspaces' 2152 | r,←⊂' that can be used on both Classic and Unicode Editions!)' 2153 | :EndIf 2154 | r,←⊂' -production remove links to source files (and execute code given in PROD instructions in buildfile)' 2155 | r,←⊂' -quiet[=n] only output actual errors (quiet=2 only writes them to log, not into session)' 2156 | r,←⊂' -save[=0|1|2] save the build workspace (overwrites TARGET''s save option). Note: we only save if no errors were logged during Build process. save=2: do NOT save, but set ⎕WSID (according to TARGET Instruction in buildfile)' 2157 | r,←⊂' -off[=0|1] )OFF after completion (if errors were logged, logfile will be created)' 2158 | r,←⊂' -target=Target override target spec from dyalogbuild file' 2159 | r,←⊂' -TestClassic check imported code for compatibility with classic editions (character set, not language features)' 2160 | r,←⊂'' 2161 | r,←⊂']',(⊃List.Group),'.',Cmd,' -??? ⍝ for description of the DyalogBuild script format' 2162 | r,←⊂'see https://github.com/Dyalog/DBuildTest/wiki/DBuild for more information' 2163 | :Case 2 2164 | r,←⊂'' 2165 | r,←⊂'each non empty line of a DyalogBuild script has the following syntax:' 2166 | r,←⊂'INSTRUCTION: argument[, Parameter1=value1, Parameter2=value2,...]' 2167 | r,←⊂' Everything after "INSTRUCTION:" may reference configuration parameters using syntax $MyParam or %MyParam% or ${MyParam}.' 2168 | r,←⊂' You can continue with any non alphabetic chars immediately following the name of the parameter, otherwise leave a blank. For example, with FOO="C:\TEMP" and Git="c:\git\", you can do' 2169 | r,←⊂' "$Foo\Goo" => "C:\TEMP\Goo", "$Git MyDir" => "c:\git\MyDir", "$Git MyDir" => "c:\git\ MyDir", "${Git}MyDir" => "c:\git\MyDir"' 2170 | r,←⊂'' 2171 | r,←⊂'INSTRUCTION may be one of the following:' 2172 | r,←⊂' DYALOGBUILD: nnn This instruction must be included and be the first one. "nnn" specifies the minimum version required to run this script.' 2173 | r,←⊂' ID: name[, Version=nnn] This instruction is purely informational and causes a log entry of "Building name" or "Building name version nnn" where "nnn" is a number.' 2174 | r,←⊂' COPY: path1, Target=path2 Copies one or more files from path1 to path2.' 2175 | r,←⊂' NS: pathname[, Target=namespace] Loads the APL object(s) defined in the file(s) matching the pattern "pathname" into "namespace" (default is #), establishing the namespace if it does not exist.' 2176 | r,←⊂' {CLASS|APL}: pathname[, Target=namespace] Loads the APL object defined in "pathname" into "namespace" (default is #).' 2177 | r,←⊂' LIB: name[, Target=namespa Loads the library utility "name" into "namespace" (default is #).' 2178 | r,←⊂' CSV: pathname, Target=matname[, ColTypes=spec] Loads the CSV file "pathname" as a matrix called "matname". "spec" corresponds to the third element of ⎕CSV''s right argument; for details, see ',⎕SE.UCMD'Tools.Help ⎕CSV -url' ⍝ CompCheck: ignore 2179 | r,←⊂' DATA: pathname, Target=namespace[, Format=type[, SetEOL=nl]] Loads the contents of the file(s) matching the pattern "pathname" into one or more variables in "namespace" (default is #). The variable(s) will be named with the base filename(s). "type" dictates how the file content of each file is interpreted, and may be one of:' 2180 | r,←⊂' charvec meaning as a simple character vector. If SetEOL=nl is specified, the lines will be separated by the chosen line ending sequence; one or more of the leftmost character codes or the corresponding decimal numbers of:' 2181 | r,←⊂' LF Line Feed ⎕UCS 10 (default)' 2182 | r,←⊂' VT Vertical Tab ⎕UCS 11' 2183 | r,←⊂' FF Form Feed ⎕UCS 12' 2184 | r,←⊂' CR Carriage Return ⎕UCS 13' 2185 | r,←⊂' NEL New Line ⎕UCS 133' 2186 | r,←⊂' LS Line Separator ⎕UCS 8282' 2187 | r,←⊂' PS Paragraph Separator ⎕UCS 8233' 2188 | r,←⊂' charvecs meaning as a vector of character vectors' 2189 | r,←⊂' charmat meaning as a character matrix' 2190 | r,←⊂' json meaning as json. The variable will be a numeric scalar, a vector, or a namespace in accordance with the JSON code in the file.' 2191 | r,←⊂' LX: expression Sets the workspace''s ⎕LX to "expression".' 2192 | r,←⊂' EXEC: expression Executes the APL expression "expression".' 2193 | r,←⊂' PROD: expression Executes the APL expression "expression" only if ]',Cmd,' was called with the -production modifier' 2194 | r,←⊂' DEFAULTS: expression Executes the APL expression "expression" in each namespace created or accessed by the NS instruction.' 2195 | r,←⊂' TARGET: wsname.dws Sets the WSID to wsname.dws so the workspace is ready to )SAVE.' 2196 | r,←⊂' Supports optional parameters:' 2197 | r,←⊂' save=0|1 (Default 0): save the workspace after a successful (=no errors were logged) build' 2198 | r,←⊂' off=0|1 (Default=0): )OFF after completion of Build. If errors were logged, a logfile (same name as the .dyalogbuild file with .log extension) will be created and exit code 1 will be set.' 2199 | r,←⊂'' 2200 | r,←⊂'see https://github.com/Dyalog/DBuildTest/wiki/DBuild for more information' 2201 | :EndSelect 2202 | 2203 | :Case 'DTest' 2204 | r←⊂'Run (a selection of) functions named test_* from a namespace, file or directory | Version ',2⊃SemVer 2205 | r,←⊂' ]',(⊃List.Group),'.',Cmd,' {||} [-halt] [-filter=string] [-off] [-quiet] [-repeat=n] [-loglvl=n] [-setup[=fn]] [-suite=file] [-teardown[=fn]] [-testlog=logfile] [-tests=] [-ts] [-timeout=t] [-trace] [-verbose] [-clear[=n]] [-coverage] [-init] [-order={0|1|"NumVec"}] -SuccessValue=...]' 2206 | :Select level 2207 | :Case 0 2208 | r,←⊂']',(⊃List.Group),'.',Cmd,' -?? ⍝ for more info' 2209 | :Case 1 2210 | r,'' 'Argument is one of:' 2211 | r,←⊂' ns namespace in the current workspace' 2212 | r,←⊂' file .dyalog file containing a namespace or a test function' 2213 | r,←⊂' path path to directory containing functions in .dyalog files' 2214 | r,←'' 'Optional modifiers are:' 2215 | r,←⊂' -clear[=n] clear ws before running tests (optionally delete nameclass n only)' 2216 | r,←⊂' -coverage enable analysis of code coverage' 2217 | r,←⊂' -filter=string only run functions whose name start with filter' 2218 | r,←⊂' -halt halt on error rather than log and continue' 2219 | r,←⊂' -init if specified test file wasn''t found, it will be initialised with a template' 2220 | r,←⊂' -loglvl control which log files we create (if value of "-off" > 0)' 2221 | r,←⊂' 1={base}.log: errors' 2222 | r,←⊂' 2={base}.warn.log warnings' 2223 | r,←⊂' 4={base}.warn.log info' 2224 | r,←⊂' 8={base}.session.log' 2225 | r,←⊂' 16={base}.session.log ONLY if test failed' 2226 | r,←⊂' 32={base}.log.json: machine readable results' 2227 | r,←⊂' Creating such a log is the ONLY way to get data on performance and memory usage of tests!' 2228 | r,←⊂' (Values are bit flags and can be added)' 2229 | r,←⊂' -order={0|1|"NumVec"} control sequence of tests: 0:random (default); 1:sequential; "NumVec":given order' 2230 | r,←⊂' -off[=0|1|2] )OFF after running the tests' 2231 | r,←⊂' 0=do not )OFF after tests' 2232 | r,←⊂' 1=)OFF after tests - creates {base}.log if errors found AND {warn|info}.log if warnings of info msgs were created (Note: depends on -loglvl)' 2233 | r,←⊂' 2=do not )OFF, but create .log files (see -loglvl)' 2234 | r,←⊂' -quiet QA mode: only output actual errors' 2235 | r,←⊂' -repeat=n repeat tests n times' 2236 | r,←⊂' -setup[=fn] run the function fn before any tests' 2237 | r,←⊂' -SuccessValue=string defines an alternate value that indicates successful execution of test (default is empty string)' 2238 | r,←⊂' (Note: this can be tricky when you want to use 0 - see wiki for details.)' 2239 | r,←⊂' -suite=file run tests defined by a .dyalogtest file (you can also pass filename directly as argument)' 2240 | r,←⊂' -teardown[=fn] run the function fn after all tests' 2241 | r,←⊂' -testlog= force name of logfile(s) (defaults to name of testfile)' 2242 | r,←⊂' -tests= comma separated list of tests to run' 2243 | r,←⊂' -timeout[=t] sets a timeout. Seconds after which test(suite)s will be terminated. (Default is 0: no timeout)' 2244 | r,←⊂' -ts add timestamp (no date) to logged messages' 2245 | r,←⊂' -trace set stop on line 1 of each test function' 2246 | r,←⊂' -verbose display more status messages while running' 2247 | r,←⊂'' 2248 | r,←⊂'see https://github.com/Dyalog/DBuildTest/wiki/DTest for more information' 2249 | :EndSelect 2250 | :Case 'GetTools4CITA' 2251 | r←⊂'Primarily an internal tool for testing with CITA | Version ',2⊃SemVer 2252 | r,←⊂' ]',(⊃List.Group),'.',Cmd,' [ns]' 2253 | :Select level 2254 | :Case 0 2255 | r,←⊂']',(⊃List.Group),'.',Cmd,' -?? ⍝ for more info' 2256 | :Case 1 2257 | r,←⊂'This copies a few tools from the DTest namespace into `⎕se._cita` and some into the namespace passed as argument (default is #)' 2258 | r,←⊂'' 2259 | r,←⊂'- SetupCompatibilityFns' 2260 | r,←⊂'- DyaVersion numeric variable holding {major}.{minor} Version of current interpreter' 2261 | r,←⊂'- APLVersion actually identifies the platform with value *nix|Win|Mac' 2262 | r,←⊂'- isChar ⍵ returns boolean value if argument is char' 2263 | r,←⊂'- isWin niladic function returning boolean to indicate if running on Windows' 2264 | r,←⊂'- ⍺ Split ⍵ split string ⍵ on positions that have value ⍺' 2265 | r,←⊂'- Init establishes additional functions' 2266 | r,←⊂'- GetDOTNETVersion - returns 4 elements to describe .NET Version that is in use:' 2267 | r,←⊂' R[1] = 0/1/2: 0=nothing, 1=.net Framework, 2=NET CORE' 2268 | r,←⊂' R[2] = Version (text vector)' 2269 | r,←⊂' R[3] = Version (identifiable x.y within [2] in numerical form)' 2270 | r,←⊂' R[4] = Textual description of the framework' 2271 | r,←⊂'- _FileTime_to_TS - legacy from the days w/o ⎕NINFO' 2272 | r,←⊂'- Nopen - helps dealing with native files' 2273 | r,←⊂'...and a few others as well as:' 2274 | r,←⊂'- base64enc' 2275 | r,←⊂'- base64dec' 2276 | r,←⊂'- base64 (subfn used by the last 2)' 2277 | r,←⊂'to encode/decode a string using base64.' 2278 | r,←⊂'The last three as well as the "DSL":' 2279 | r,←⊂'- Because' 2280 | r,←⊂'- Fail' 2281 | r,←⊂'- Check' 2282 | r,←⊂'- IfNot' 2283 | r,←⊂'- IsNotElement' 2284 | r,←⊂'- eis' 2285 | r,←⊂'- Assert' 2286 | r,←⊂' will also be copied into the ns passed as argument (# by default)' 2287 | :EndSelect 2288 | :EndSelect 2289 | ∇ 2290 | 2291 | :namespace _cita 2292 | 2293 | ∇ Write2Log txt;file 2294 | ⍝ needs name of test 2295 | file←GetCITA_Log 1 2296 | (⊂txt)⎕NPUT file 2 2297 | ∇ 2298 | 2299 | ∇ R←GetCITA_Log signal;z 2300 | ⍝ signal: should we ⎕SIGNAL an error if no config file is found? (default=1) 2301 | :If 4=⍴R←⎕SE.Dyalog.Utils.ExpandConfig'.log',⍨2 ⎕NQ'.' 'GetEnvironment' 'CITA_LOG' 2302 | ⍝ ⎕←2 ⎕NQ'.' 'GetCommandLineArgs' ⍝ spit out commandline into the session - maybe it help diagnosing the problem... 2303 | :If 1∊z←∊⎕RSI{0::0 ⋄ 2=⍺.⎕NC ⍵:0<⍺⍎⍵ ⋄ 0}¨⊂'CITA_LOG' ⍝ CompCheck: ignore / search calling environment for variable CITA_LOG 2304 | R←((z⍳2)⊃⎕RSI).CITA_LOG ⍝ CompCheck: ignore 2305 | :Else 2306 | ⍝ alternatively use name of test 2307 | :If 0<≢R←2 ⎕NQ'.' 'GetEnvironment' 'CITATEST' 2308 | R←∊(2↑⎕NPARTS R),'.CITA.log' 2309 | :Else 2310 | :If signal 2311 | 'Found no CITA_LOG in Environment - this dws is supposed to be called from CITA which should have passed the right commandline'⎕SIGNAL 11 2312 | :EndIf 2313 | :EndIf 2314 | :EndIf 2315 | :EndIf 2316 | ∇ 2317 | 2318 | ∇ {file}←{msg}_LogStatus status;file;⎕ML;rc;t;log;z;l2;Myrc 2319 | ⍝ (⍳100)⎕trace 1⊃⎕si 2320 | ⍝ A step (setup|test|teardown) is finished, report its status to the engine. 2321 | ⍝ msg allows inject of a message into the file, otherwise an empty file will be created. 2322 | ⍝ options: 2323 | ⍝ fail | ok | error <------ these are the extensions of the status file 2324 | ⍝ no | yes | {for ⍵ 2325 | ⍝ | success | {alternative values 2326 | ⍝ 0 | 1 |¯1 numeric codes for ⍵ 2327 | ⍝ bonus option: any other text passed as status will be used as file extension... 2328 | ⍝ 2329 | ⍝ logging a status will save the session log AND ⎕OFF (returncode as given in status[2] OR 31=success, 32=failure, 33=error) 2330 | ⍝ returncode=¯42 will NOT off (but will write the log file!) 2331 | ⎕ML←1 2332 | :If 0=⎕NC'msg' ⋄ msg←'' ⋄ :EndIf 2333 | file←∊2↑⎕NPARTS GetCITA_Log 1 2334 | :If 1<⍴,status 2335 | :AndIf 0={⎕ML←0 ⋄ ∊⍵}2⊃status 2336 | (status MYrc)←status 2337 | :EndIf 2338 | :If isChar status ⍝ decode status from string 2339 | ⍝ translate known status into "standardized" extensions (that have a certain meaning in CITA) 2340 | :If ∨/(⊂lc status){(0<''⍴⍴⍺)∧⍺≡(''⍴⍴⍺)↑⍵}¨'failure' 'no' 2341 | status←'fail' ⋄ rc←32 2342 | :ElseIf ∨/(⊂lc status){(0<''⍴⍴⍺)∧⍺≡(''⍴⍴⍺)↑⍵}¨'success' 'ok' 'yes' 2343 | status←'ok' ⋄ rc←31 2344 | :ElseIf ∨/(⊂lc status){(0<''⍴⍴⍺)∧⍺≡(''⍴⍴⍺)↑⍵}¨⊂'error' 2345 | status←'err' ⋄ rc←33 2346 | :Else ⍝ otherwise just use the value given... 2347 | :EndIf 2348 | :Else 2349 | status←1⊃1↑status ⍝ ensure we have a numeric scalar 2350 | status←status×status∊¯1 1 2351 | rc←(2+status)⊃33 32 31 2352 | status←(2+status)⊃'err' 'fail' 'ok' 2353 | :EndIf 2354 | :If 2=⎕NC'MYrc' 2355 | rc←MYrc 2356 | :EndIf 2357 | ⍝ we're intentionally not passing ⍵[2]as 1 to force overwrite - because this is supposed to be called once only! 2358 | ⍝ So if it crashes...that is well deserved... 2359 | ⍝ write status file 2360 | file←file,'.',status 2361 | :If ⎕NEXISTS file 2362 | (⊂msg)⎕NPUT file,'-exists',⍕1+≢⊃0(⎕NINFO ⎕OPT'Wildcard' 1)(file,'*') 2363 | :Else 2364 | (⊂msg)⎕NPUT file 2365 | :EndIf 2366 | 2367 | :If 2=⎕NC'⎕se._cita._memStats' 2368 | :Trap 0 2369 | t←{0::((1 3⍴0 ¯1 0)⎕FSTAC t)⊢t←⍵ ⎕FCREATE 0 ⋄ ⍵ ⎕FSTIE 0}(1⊃⎕NPARTS file),'MemRep' 2370 | ⎕SE._cita._memStats ⎕FAPPEND t 2371 | ⎕SE._cita.∆cpu ⎕FAPPEND t 2372 | ⎕FUNTIE t 2373 | :Else 2374 | ⎕←'Caught error writing mem stats into ',(1⊃⎕NPARTS file),'MemRep:' 2375 | ⎕←(⎕JSON ⎕OPT'Compact' 0)⎕DMX 2376 | :EndTrap 2377 | :EndIf 2378 | 2379 | ⍝ write the logfile 2380 | :Trap 0 2381 | log←⎕SE ⎕WG'Log' 2382 | :Trap 1 2383 | :If 2=⎕SE.⎕NC'RunCITA∆OldLog' 2384 | z←⎕SE.RunCITA∆OldLog NrOfCommonLines log 2385 | log,←⊂'Old log and new log have ',(⍕z),' common lines that were ignored!' 2386 | ⍝ z←0 ⍝ TODO: remove this 2387 | :Else 2388 | z←0 2389 | log,←⊂'Did not find ⎕se.RunCITA∆OldLog - not ignoring anything from the old log' 2390 | :EndIf 2391 | log←z↓log 2392 | log←∊log,¨⊂NL 2393 | 2394 | :Else 2395 | :Trap 0 2396 | ⎕←'trapped WSFULL! EN=',⎕EN 2397 | l2←'' 2398 | :While 0<⍴log 2399 | l2,←1⊃log 2400 | l2,←NL 2401 | log←1↓log 2402 | :EndWhile 2403 | log←l2 2404 | ⎕EX'l2' 2405 | :Else 2406 | ⎕←'Unfixable WS FULL!' 2407 | →0 2408 | :EndTrap 2409 | :EndTrap 2410 | log,←'TS.End=',⍕⎕TS 2411 | file←(1⊃⎕NPARTS file),({(2>+\⍵='.')/⍵}2⊃⎕NPARTS file),'.txt' 2412 | (⊂log)⎕NPUT file 1 2413 | :Else 2414 | ⎕←'*** Error while attempting to write sessionlog to a file:' 2415 | ⎕←(⎕JSON ⎕OPT'Compact' 0)⎕DMX 2416 | ⎕←'file=',file 2417 | ⎕←'log=',log 2418 | ⎕←'si' ⋄ ⎕←⍕⎕XSI,[1.5]⎕LC 2419 | :EndTrap 2420 | ⍝ :If rc≠¯42 2421 | ⎕OFF(|rc) 2422 | ⍝ :EndIf 2423 | ⍝1300⌶77 ⍝ Andy 2424 | ∇ 2425 | 2426 | ⍝ Define Success'blablabla' and Failure'blabla' and Error'blasbla'as shortcuts to 'blabla'_LogStatus 1|0|¯1 2427 | Success←_LogStatus∘1 2428 | Failure←_LogStatus∘0 2429 | Error←_LogStatus∘¯1 2430 | 2431 | 2432 | ∇ {R}←{AddPerf}RecordMemStats suffix;facts;pFmt;r;f 2433 | ⍝ AddPerf=0 or missing: don't care about performance 2434 | ⍝ 1 : remember ⎕AI[2 3] 2435 | ⍝ 2 : report ∆ of ⎕AI[2 3] 2436 | pFmt←{w←⍕⍵ ⋄ t←0.333×≢w ⋄ {(¯1+⊃where w∊⎕D)↓w},',',(⌽3,⌈t)⍴(-3×⌈t)↑w} ⍝ pragmatic way to inject "," w/o worrying abt formatstrings or decimals (use TamStat's FmtX if we should need more) 2437 | R←⍬ 2438 | :If 2=⎕NC'AddPerf' 2439 | :Select AddPerf 2440 | :Case 1 ⋄ ∆cpu←⎕AI[2 3] ⍝ intentionally leaves ∆cpu behind in ⎕se._cita 2441 | :Case 2 ⋄ ∆cpu←⎕AI[2 3]-∆cpu ⋄ R,←⊂('Perf: ∆ AI[2 3]=',∊' ',¨pFmt¨∆cpu)'' 2442 | :EndSelect 2443 | :EndIf 2444 | facts←(0 'WS available')(1 'WS used')(2 'Compactions')(3 'Successful garbage collections')(4 'Garbage pockets')(9 'Free pockets')(10 'Used pockets') 2445 | facts,←(12 'Sediment size')(13 'WS alloc')(14 'Max WS')(15 'Limit on min ws alloc')(16 'Limit on max ws alloc')(19 '2002⌶ calls') 2446 | 2447 | r←⍬ 2448 | :For f :In facts 2449 | r,←⊂f,(2000⌶)1⊃f 2450 | :EndFor 2451 | r,←⊂¯1 'APLVersion'(2⊃'.'⎕WG'aplversion') 2452 | 2453 | R,←⊂'Memory manager statistics',((0<≢suffix)⍴' '),suffix,':' 2454 | R,←r 2455 | :If 0=⎕NC'⎕se._cita._memStats' 2456 | ⎕SE._cita._memStats←R 2457 | :Else 2458 | ⎕SE._cita._memStats,←R 2459 | :EndIf 2460 | ∇ 2461 | 2462 | 2463 | NrOfCommonLines←{+/∧\{⍵=⍵[1]+¯1+⍳≢⍵}⍺{((≢⍺)⍴⍋⍋⍺⍳⍺⍪⍵)⍳(≢⍵)⍴⍋⍋⍺⍳⍵⍪⍺}⍵} 2464 | :EndNamespace 2465 | :EndSection 2466 | :EndNamespace ⍝ DyalogBuild $Revision$ 2467 | --------------------------------------------------------------------------------