2024-05-20 18:26:45 +00:00
package parser
2024-01-05 22:04:31 +00:00
import (
2024-04-22 22:37:14 +00:00
"bytes"
2024-05-20 18:26:45 +00:00
"encoding/binary"
2024-11-14 21:59:44 +00:00
"errors"
2024-04-22 22:37:14 +00:00
"fmt"
"io"
2024-01-05 22:04:31 +00:00
"strings"
"testing"
2024-05-20 18:26:45 +00:00
"unicode/utf16"
2024-01-05 22:04:31 +00:00
"github.com/stretchr/testify/assert"
2024-05-22 05:21:04 +00:00
"github.com/stretchr/testify/require"
2024-06-13 18:09:22 +00:00
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/unicode"
2024-01-05 22:04:31 +00:00
)
2024-04-30 17:55:19 +00:00
func TestParseFileFile ( t * testing . T ) {
2024-01-05 22:04:31 +00:00
input := `
FROM model1
ADAPTER adapter1
LICENSE MIT
PARAMETER param1 value1
PARAMETER param2 value2
2024-07-01 16:43:49 +00:00
TEMPLATE "" " { { if . System } } < | start_header_id | > system < | end_header_id | >
{ { . System } } < | eot_id | > { { end } } { { if . Prompt } } < | start_header_id | > user < | end_header_id | >
{ { . Prompt } } < | eot_id | > { { end } } < | start_header_id | > assistant < | end_header_id | >
{ { . Response } } < | eot_id | > "" "
2024-01-05 22:04:31 +00:00
`
reader := strings . NewReader ( input )
2024-04-30 17:55:19 +00:00
modelfile , err := ParseFile ( reader )
2024-05-22 05:21:04 +00:00
require . NoError ( t , err )
2024-01-05 22:04:31 +00:00
expectedCommands := [ ] Command {
{ Name : "model" , Args : "model1" } ,
{ Name : "adapter" , Args : "adapter1" } ,
{ Name : "license" , Args : "MIT" } ,
{ Name : "param1" , Args : "value1" } ,
{ Name : "param2" , Args : "value2" } ,
2024-07-01 16:43:49 +00:00
{ Name : "template" , Args : "{{ if .System }}<|start_header_id|>system<|end_header_id|>\n\n{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>\n\n{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>\n\n{{ .Response }}<|eot_id|>" } ,
2024-01-05 22:04:31 +00:00
}
2024-04-30 17:55:19 +00:00
assert . Equal ( t , expectedCommands , modelfile . Commands )
2024-01-05 22:04:31 +00:00
}
2024-07-01 18:07:48 +00:00
func TestParseFileTrimSpace ( t * testing . T ) {
input := `
FROM " model 1"
ADAPTER adapter3
LICENSE "MIT "
PARAMETER param1 value1
PARAMETER param2 value2
TEMPLATE "" " { { if . System } } < | start_header_id | > system < | end_header_id | >
{ { . System } } < | eot_id | > { { end } } { { if . Prompt } } < | start_header_id | > user < | end_header_id | >
{ { . Prompt } } < | eot_id | > { { end } } < | start_header_id | > assistant < | end_header_id | >
{ { . Response } } < | eot_id | > "" "
`
reader := strings . NewReader ( input )
modelfile , err := ParseFile ( reader )
require . NoError ( t , err )
expectedCommands := [ ] Command {
{ Name : "model" , Args : " model 1" } ,
{ Name : "adapter" , Args : "adapter3" } ,
{ Name : "license" , Args : "MIT " } ,
{ Name : "param1" , Args : "value1" } ,
{ Name : "param2" , Args : "value2" } ,
{ Name : "template" , Args : " {{ if .System }}<|start_header_id|>system<|end_header_id|>\n\n{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>\n\n{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>\n\n{{ .Response }}<|eot_id|> " } ,
}
assert . Equal ( t , expectedCommands , modelfile . Commands )
}
2024-04-30 17:55:19 +00:00
func TestParseFileFrom ( t * testing . T ) {
2024-08-01 21:52:15 +00:00
cases := [ ] struct {
2024-04-24 23:12:56 +00:00
input string
expected [ ] Command
err error
} {
2024-06-27 17:59:15 +00:00
{
"FROM \"FOO BAR \"" ,
[ ] Command { { Name : "model" , Args : "FOO BAR " } } ,
nil ,
} ,
{
"FROM \"FOO BAR\"\nPARAMETER param1 value1" ,
[ ] Command { { Name : "model" , Args : "FOO BAR" } , { Name : "param1" , Args : "value1" } } ,
nil ,
} ,
{
"FROM FOOO BAR " ,
[ ] Command { { Name : "model" , Args : "FOOO BAR" } } ,
nil ,
} ,
{
"FROM /what/is/the path " ,
[ ] Command { { Name : "model" , Args : "/what/is/the path" } } ,
nil ,
} ,
2024-04-24 23:12:56 +00:00
{
"FROM foo" ,
[ ] Command { { Name : "model" , Args : "foo" } } ,
nil ,
} ,
{
"FROM /path/to/model" ,
[ ] Command { { Name : "model" , Args : "/path/to/model" } } ,
nil ,
} ,
{
"FROM /path/to/model/fp16.bin" ,
[ ] Command { { Name : "model" , Args : "/path/to/model/fp16.bin" } } ,
nil ,
} ,
{
"FROM llama3:latest" ,
[ ] Command { { Name : "model" , Args : "llama3:latest" } } ,
nil ,
} ,
{
"FROM llama3:7b-instruct-q4_K_M" ,
[ ] Command { { Name : "model" , Args : "llama3:7b-instruct-q4_K_M" } } ,
nil ,
} ,
{
"" , nil , errMissingFrom ,
} ,
{
"PARAMETER param1 value1" ,
nil ,
errMissingFrom ,
} ,
{
"PARAMETER param1 value1\nFROM foo" ,
[ ] Command { { Name : "param1" , Args : "value1" } , { Name : "model" , Args : "foo" } } ,
nil ,
} ,
2024-06-27 17:59:15 +00:00
{
"PARAMETER what the \nFROM lemons make lemonade " ,
2024-06-27 18:18:38 +00:00
[ ] Command { { Name : "what" , Args : "the" } , { Name : "model" , Args : "lemons make lemonade" } } ,
2024-06-27 17:59:15 +00:00
nil ,
} ,
2024-04-24 23:12:56 +00:00
}
2024-01-05 22:04:31 +00:00
2024-04-24 23:12:56 +00:00
for _ , c := range cases {
t . Run ( "" , func ( t * testing . T ) {
2024-04-30 17:55:19 +00:00
modelfile , err := ParseFile ( strings . NewReader ( c . input ) )
2024-05-22 05:21:04 +00:00
require . ErrorIs ( t , err , c . err )
2024-04-30 17:55:19 +00:00
if modelfile != nil {
assert . Equal ( t , c . expected , modelfile . Commands )
}
2024-04-24 23:12:56 +00:00
} )
}
2024-01-05 22:04:31 +00:00
}
2024-04-30 17:55:19 +00:00
func TestParseFileParametersMissingValue ( t * testing . T ) {
2024-01-05 22:04:31 +00:00
input := `
FROM foo
PARAMETER param1
`
reader := strings . NewReader ( input )
2024-04-30 17:55:19 +00:00
_ , err := ParseFile ( reader )
2024-05-22 05:21:04 +00:00
require . ErrorIs ( t , err , io . ErrUnexpectedEOF )
2024-01-05 22:04:31 +00:00
}
2024-01-25 20:12:36 +00:00
2024-04-30 17:55:19 +00:00
func TestParseFileBadCommand ( t * testing . T ) {
2024-04-27 00:11:47 +00:00
input := `
FROM foo
BADCOMMAND param1 value1
`
2024-11-14 21:59:44 +00:00
parserError := & ParserError {
LineNumber : 3 ,
Msg : errInvalidCommand . Error ( ) ,
}
2024-04-30 17:55:19 +00:00
_ , err := ParseFile ( strings . NewReader ( input ) )
2024-11-14 21:59:44 +00:00
if ! errors . As ( err , & parserError ) {
t . Errorf ( "unexpected error: expected: %s, actual: %s" , parserError . Error ( ) , err . Error ( ) )
}
2024-04-27 00:11:47 +00:00
}
2024-04-30 17:55:19 +00:00
func TestParseFileMessages ( t * testing . T ) {
2024-08-01 21:52:15 +00:00
cases := [ ] struct {
2024-04-22 22:37:14 +00:00
input string
expected [ ] Command
err error
} {
{
`
FROM foo
2024-04-30 17:55:19 +00:00
MESSAGE system You are a file parser . Always parse things .
2024-04-22 22:37:14 +00:00
` ,
[ ] Command {
{ Name : "model" , Args : "foo" } ,
2024-04-30 17:55:19 +00:00
{ Name : "message" , Args : "system: You are a file parser. Always parse things." } ,
2024-04-22 22:37:14 +00:00
} ,
nil ,
} ,
{
`
2024-01-25 20:12:36 +00:00
FROM foo
2024-04-30 17:55:19 +00:00
MESSAGE system You are a file parser . Always parse things . ` ,
2024-04-25 02:17:26 +00:00
[ ] Command {
{ Name : "model" , Args : "foo" } ,
2024-04-30 17:55:19 +00:00
{ Name : "message" , Args : "system: You are a file parser. Always parse things." } ,
2024-04-25 02:17:26 +00:00
} ,
nil ,
} ,
{
`
FROM foo
2024-04-30 17:55:19 +00:00
MESSAGE system You are a file parser . Always parse things .
2024-01-25 20:12:36 +00:00
MESSAGE user Hey there !
MESSAGE assistant Hello , I want to parse all the things !
2024-04-22 22:37:14 +00:00
` ,
[ ] Command {
{ Name : "model" , Args : "foo" } ,
2024-04-30 17:55:19 +00:00
{ Name : "message" , Args : "system: You are a file parser. Always parse things." } ,
2024-04-22 22:37:14 +00:00
{ Name : "message" , Args : "user: Hey there!" } ,
{ Name : "message" , Args : "assistant: Hello, I want to parse all the things!" } ,
} ,
nil ,
} ,
{
`
FROM foo
MESSAGE system "" "
2024-04-30 17:55:19 +00:00
You are a multiline file parser . Always parse things .
2024-04-22 22:37:14 +00:00
"" "
` ,
[ ] Command {
{ Name : "model" , Args : "foo" } ,
2024-04-30 17:55:19 +00:00
{ Name : "message" , Args : "system: \nYou are a multiline file parser. Always parse things.\n" } ,
2024-04-22 22:37:14 +00:00
} ,
nil ,
} ,
{
`
FROM foo
MESSAGE badguy I ' m a bad guy !
` ,
nil ,
2024-11-14 21:59:44 +00:00
& ParserError {
LineNumber : 3 ,
Msg : errInvalidMessageRole . Error ( ) ,
} ,
2024-04-22 22:37:14 +00:00
} ,
{
`
FROM foo
MESSAGE system
` ,
nil ,
io . ErrUnexpectedEOF ,
} ,
{
`
FROM foo
MESSAGE system ` ,
nil ,
io . ErrUnexpectedEOF ,
} ,
}
2024-01-25 20:12:36 +00:00
2024-11-14 21:59:44 +00:00
for _ , tt := range cases {
2024-04-22 22:37:14 +00:00
t . Run ( "" , func ( t * testing . T ) {
2024-11-14 21:59:44 +00:00
modelfile , err := ParseFile ( strings . NewReader ( tt . input ) )
2024-04-30 17:55:19 +00:00
if modelfile != nil {
2024-11-14 21:59:44 +00:00
assert . Equal ( t , tt . expected , modelfile . Commands )
}
if tt . err == nil {
if err != nil {
t . Fatalf ( "expected no error, but got %v" , err )
}
return
}
switch tt . err . ( type ) {
case * ParserError :
var pErr * ParserError
if errors . As ( err , & pErr ) {
// got the correct type of error
return
}
}
if errors . Is ( err , tt . err ) {
return
2024-04-30 17:55:19 +00:00
}
2024-11-14 21:59:44 +00:00
t . Fatalf ( "unexpected error: expected: %v, actual: %v" , tt . err , err )
2024-04-22 22:37:14 +00:00
} )
}
}
2024-01-25 20:12:36 +00:00
2024-04-30 17:55:19 +00:00
func TestParseFileQuoted ( t * testing . T ) {
2024-08-01 21:52:15 +00:00
cases := [ ] struct {
2024-04-22 22:37:14 +00:00
multiline string
expected [ ] Command
err error
} {
{
`
FROM foo
2024-04-25 02:17:26 +00:00
SYSTEM "" "
2024-04-22 22:37:14 +00:00
This is a
2024-04-25 02:17:26 +00:00
multiline system .
2024-04-22 22:37:14 +00:00
"" "
` ,
[ ] Command {
{ Name : "model" , Args : "foo" } ,
2024-04-25 02:17:26 +00:00
{ Name : "system" , Args : "\nThis is a\nmultiline system.\n" } ,
2024-04-22 22:37:14 +00:00
} ,
nil ,
} ,
{
`
FROM foo
2024-04-25 02:17:26 +00:00
SYSTEM "" "
2024-04-22 22:37:14 +00:00
This is a
2024-04-25 02:17:26 +00:00
multiline system . "" "
2024-04-22 22:37:14 +00:00
` ,
[ ] Command {
{ Name : "model" , Args : "foo" } ,
2024-04-25 02:17:26 +00:00
{ Name : "system" , Args : "\nThis is a\nmultiline system." } ,
2024-04-22 22:37:14 +00:00
} ,
nil ,
} ,
{
`
FROM foo
2024-04-25 02:17:26 +00:00
SYSTEM "" " This is a
multiline system . "" "
2024-04-22 22:37:14 +00:00
` ,
[ ] Command {
{ Name : "model" , Args : "foo" } ,
2024-04-25 02:17:26 +00:00
{ Name : "system" , Args : "This is a\nmultiline system." } ,
2024-04-22 22:37:14 +00:00
} ,
nil ,
} ,
{
`
FROM foo
2024-04-25 02:17:26 +00:00
SYSTEM "" "This is a multiline system." ""
2024-04-22 22:37:14 +00:00
` ,
[ ] Command {
{ Name : "model" , Args : "foo" } ,
2024-04-25 02:17:26 +00:00
{ Name : "system" , Args : "This is a multiline system." } ,
2024-04-22 22:37:14 +00:00
} ,
nil ,
} ,
{
`
FROM foo
2024-04-25 02:17:26 +00:00
SYSTEM "" "This is a multiline system." "
2024-04-22 22:37:14 +00:00
` ,
nil ,
io . ErrUnexpectedEOF ,
} ,
{
`
FROM foo
2024-04-25 02:17:26 +00:00
SYSTEM "
2024-04-22 22:37:14 +00:00
` ,
nil ,
io . ErrUnexpectedEOF ,
} ,
{
`
FROM foo
2024-04-25 02:17:26 +00:00
SYSTEM "" "
This is a multiline system with "quotes" .
2024-04-22 22:37:14 +00:00
"" "
` ,
[ ] Command {
{ Name : "model" , Args : "foo" } ,
2024-04-25 02:17:26 +00:00
{ Name : "system" , Args : "\nThis is a multiline system with \"quotes\".\n" } ,
2024-04-22 22:37:14 +00:00
} ,
nil ,
} ,
{
`
FROM foo
2024-04-25 02:17:26 +00:00
SYSTEM "" "" ""
2024-04-22 22:37:14 +00:00
` ,
[ ] Command {
{ Name : "model" , Args : "foo" } ,
2024-04-25 02:17:26 +00:00
{ Name : "system" , Args : "" } ,
2024-04-22 22:37:14 +00:00
} ,
nil ,
} ,
{
`
FROM foo
2024-04-25 02:17:26 +00:00
SYSTEM ""
2024-04-22 22:37:14 +00:00
` ,
[ ] Command {
{ Name : "model" , Args : "foo" } ,
2024-04-25 02:17:26 +00:00
{ Name : "system" , Args : "" } ,
2024-04-22 22:37:14 +00:00
} ,
nil ,
} ,
{
`
FROM foo
2024-04-25 02:17:26 +00:00
SYSTEM "'"
2024-04-22 22:37:14 +00:00
` ,
[ ] Command {
{ Name : "model" , Args : "foo" } ,
2024-04-25 02:17:26 +00:00
{ Name : "system" , Args : "'" } ,
2024-04-22 22:37:14 +00:00
} ,
nil ,
} ,
2024-04-24 23:12:56 +00:00
{
`
FROM foo
2024-04-25 02:17:26 +00:00
SYSTEM "" "''" ' "" ' "" '"' ''' ' "" ' "" ' "" "
2024-04-24 23:12:56 +00:00
` ,
[ ] Command {
{ Name : "model" , Args : "foo" } ,
2024-04-25 02:17:26 +00:00
{ Name : "system" , Args : ` ''"'""'""'"'''''""'""' ` } ,
} ,
nil ,
} ,
{
`
FROM foo
TEMPLATE "" "
{ { . Prompt } }
"" " ` ,
[ ] Command {
{ Name : "model" , Args : "foo" } ,
{ Name : "template" , Args : "\n{{ .Prompt }}\n" } ,
2024-04-24 23:12:56 +00:00
} ,
nil ,
} ,
2024-01-25 20:12:36 +00:00
}
2024-04-22 22:37:14 +00:00
for _ , c := range cases {
t . Run ( "" , func ( t * testing . T ) {
2024-04-30 17:55:19 +00:00
modelfile , err := ParseFile ( strings . NewReader ( c . multiline ) )
2024-05-22 05:21:04 +00:00
require . ErrorIs ( t , err , c . err )
2024-04-30 17:55:19 +00:00
if modelfile != nil {
assert . Equal ( t , c . expected , modelfile . Commands )
}
2024-04-22 22:37:14 +00:00
} )
}
2024-01-25 20:12:36 +00:00
}
2024-04-30 17:55:19 +00:00
func TestParseFileParameters ( t * testing . T ) {
2024-08-01 21:52:15 +00:00
cases := map [ string ] struct {
2024-04-24 23:12:56 +00:00
name , value string
} {
"numa true" : { "numa" , "true" } ,
"num_ctx 1" : { "num_ctx" , "1" } ,
"num_batch 1" : { "num_batch" , "1" } ,
"num_gqa 1" : { "num_gqa" , "1" } ,
"num_gpu 1" : { "num_gpu" , "1" } ,
"main_gpu 1" : { "main_gpu" , "1" } ,
"low_vram true" : { "low_vram" , "true" } ,
"logits_all true" : { "logits_all" , "true" } ,
"vocab_only true" : { "vocab_only" , "true" } ,
"use_mmap true" : { "use_mmap" , "true" } ,
"use_mlock true" : { "use_mlock" , "true" } ,
"num_thread 1" : { "num_thread" , "1" } ,
"num_keep 1" : { "num_keep" , "1" } ,
"seed 1" : { "seed" , "1" } ,
"num_predict 1" : { "num_predict" , "1" } ,
"top_k 1" : { "top_k" , "1" } ,
"top_p 1.0" : { "top_p" , "1.0" } ,
2024-07-27 21:37:40 +00:00
"min_p 0.05" : { "min_p" , "0.05" } ,
2024-04-24 23:12:56 +00:00
"tfs_z 1.0" : { "tfs_z" , "1.0" } ,
"typical_p 1.0" : { "typical_p" , "1.0" } ,
"repeat_last_n 1" : { "repeat_last_n" , "1" } ,
"temperature 1.0" : { "temperature" , "1.0" } ,
"repeat_penalty 1.0" : { "repeat_penalty" , "1.0" } ,
"presence_penalty 1.0" : { "presence_penalty" , "1.0" } ,
"frequency_penalty 1.0" : { "frequency_penalty" , "1.0" } ,
"mirostat 1" : { "mirostat" , "1" } ,
"mirostat_tau 1.0" : { "mirostat_tau" , "1.0" } ,
"mirostat_eta 1.0" : { "mirostat_eta" , "1.0" } ,
"penalize_newline true" : { "penalize_newline" , "true" } ,
"stop ### User:" : { "stop" , "### User:" } ,
2024-06-27 18:18:38 +00:00
"stop ### User: " : { "stop" , "### User:" } ,
2024-04-24 23:12:56 +00:00
"stop \"### User:\"" : { "stop" , "### User:" } ,
"stop \"### User: \"" : { "stop" , "### User: " } ,
"stop \"\"\"### User:\"\"\"" : { "stop" , "### User:" } ,
"stop \"\"\"### User:\n\"\"\"" : { "stop" , "### User:\n" } ,
"stop <|endoftext|>" : { "stop" , "<|endoftext|>" } ,
"stop <|eot_id|>" : { "stop" , "<|eot_id|>" } ,
"stop </s>" : { "stop" , "</s>" } ,
2024-04-22 22:37:14 +00:00
}
2024-01-25 20:12:36 +00:00
2024-04-24 23:12:56 +00:00
for k , v := range cases {
t . Run ( k , func ( t * testing . T ) {
2024-04-22 22:37:14 +00:00
var b bytes . Buffer
fmt . Fprintln ( & b , "FROM foo" )
2024-04-24 23:12:56 +00:00
fmt . Fprintln ( & b , "PARAMETER" , k )
2024-04-30 17:55:19 +00:00
modelfile , err := ParseFile ( & b )
2024-05-22 05:21:04 +00:00
require . NoError ( t , err )
2024-04-24 23:12:56 +00:00
assert . Equal ( t , [ ] Command {
{ Name : "model" , Args : "foo" } ,
{ Name : v . name , Args : v . value } ,
2024-04-30 17:55:19 +00:00
} , modelfile . Commands )
2024-04-22 22:37:14 +00:00
} )
}
}
2024-04-30 17:55:19 +00:00
func TestParseFileComments ( t * testing . T ) {
2024-08-01 21:52:15 +00:00
cases := [ ] struct {
2024-04-22 22:37:14 +00:00
input string
expected [ ] Command
} {
{
`
# comment
2024-01-25 20:12:36 +00:00
FROM foo
2024-04-22 22:37:14 +00:00
` ,
[ ] Command {
{ Name : "model" , Args : "foo" } ,
} ,
} ,
}
2024-01-25 20:12:36 +00:00
2024-04-22 22:37:14 +00:00
for _ , c := range cases {
t . Run ( "" , func ( t * testing . T ) {
2024-04-30 17:55:19 +00:00
modelfile , err := ParseFile ( strings . NewReader ( c . input ) )
2024-05-22 05:21:04 +00:00
require . NoError ( t , err )
2024-04-30 17:55:19 +00:00
assert . Equal ( t , c . expected , modelfile . Commands )
2024-04-22 22:37:14 +00:00
} )
}
2024-01-25 20:12:36 +00:00
}
2024-04-25 01:49:14 +00:00
2024-04-30 17:55:19 +00:00
func TestParseFileFormatParseFile ( t * testing . T ) {
2024-08-01 21:52:15 +00:00
cases := [ ] string {
2024-04-25 01:49:14 +00:00
`
FROM foo
ADAPTER adapter1
LICENSE MIT
PARAMETER param1 value1
PARAMETER param2 value2
TEMPLATE template1
2024-04-30 17:55:19 +00:00
MESSAGE system You are a file parser . Always parse things .
2024-04-25 01:49:14 +00:00
MESSAGE user Hey there !
MESSAGE assistant Hello , I want to parse all the things !
` ,
`
FROM foo
ADAPTER adapter1
LICENSE MIT
PARAMETER param1 value1
PARAMETER param2 value2
TEMPLATE template1
MESSAGE system "" "
You are a store greeter . Always responsed with "Hello!" .
"" "
MESSAGE user Hey there !
MESSAGE assistant Hello , I want to parse all the things !
` ,
`
FROM foo
ADAPTER adapter1
LICENSE "" "
Very long and boring legal text .
Blah blah blah .
"Oh look, a quote!"
"" "
PARAMETER param1 value1
PARAMETER param2 value2
TEMPLATE template1
MESSAGE system "" "
You are a store greeter . Always responsed with "Hello!" .
"" "
MESSAGE user Hey there !
MESSAGE assistant Hello , I want to parse all the things !
2024-05-07 16:59:21 +00:00
` ,
`
FROM foo
SYSTEM ""
2024-04-25 01:49:14 +00:00
` ,
}
for _ , c := range cases {
t . Run ( "" , func ( t * testing . T ) {
2024-04-30 17:55:19 +00:00
modelfile , err := ParseFile ( strings . NewReader ( c ) )
2024-05-22 05:21:04 +00:00
require . NoError ( t , err )
2024-04-25 01:49:14 +00:00
2024-04-30 17:55:19 +00:00
modelfile2 , err := ParseFile ( strings . NewReader ( modelfile . String ( ) ) )
2024-05-22 05:21:04 +00:00
require . NoError ( t , err )
2024-04-25 01:49:14 +00:00
2024-04-30 17:55:19 +00:00
assert . Equal ( t , modelfile , modelfile2 )
2024-04-25 01:49:14 +00:00
} )
}
}
2024-05-20 18:26:45 +00:00
func TestParseFileUTF16ParseFile ( t * testing . T ) {
data := ` FROM bob
PARAMETER param1 1
PARAMETER param2 4096
SYSTEM You are a utf16 file .
`
expected := [ ] Command {
{ Name : "model" , Args : "bob" } ,
{ Name : "param1" , Args : "1" } ,
{ Name : "param2" , Args : "4096" } ,
{ Name : "system" , Args : "You are a utf16 file." } ,
}
2024-06-13 18:09:22 +00:00
t . Run ( "le" , func ( t * testing . T ) {
var b bytes . Buffer
require . NoError ( t , binary . Write ( & b , binary . LittleEndian , [ ] byte { 0xff , 0xfe } ) )
require . NoError ( t , binary . Write ( & b , binary . LittleEndian , utf16 . Encode ( [ ] rune ( data ) ) ) )
2024-05-20 18:26:45 +00:00
2024-06-13 18:09:22 +00:00
actual , err := ParseFile ( & b )
require . NoError ( t , err )
2024-05-20 18:26:45 +00:00
2024-06-13 18:09:22 +00:00
assert . Equal ( t , expected , actual . Commands )
} )
t . Run ( "be" , func ( t * testing . T ) {
var b bytes . Buffer
require . NoError ( t , binary . Write ( & b , binary . BigEndian , [ ] byte { 0xfe , 0xff } ) )
require . NoError ( t , binary . Write ( & b , binary . BigEndian , utf16 . Encode ( [ ] rune ( data ) ) ) )
actual , err := ParseFile ( & b )
require . NoError ( t , err )
assert . Equal ( t , expected , actual . Commands )
} )
}
func TestParseMultiByte ( t * testing . T ) {
input := ` FROM test
SYSTEM 你好 👋 `
expect := [ ] Command {
{ Name : "model" , Args : "test" } ,
{ Name : "system" , Args : "你好👋" } ,
}
encodings := [ ] encoding . Encoding {
unicode . UTF8 ,
unicode . UTF16 ( unicode . LittleEndian , unicode . UseBOM ) ,
unicode . UTF16 ( unicode . BigEndian , unicode . UseBOM ) ,
}
for _ , encoding := range encodings {
t . Run ( fmt . Sprintf ( "%s" , encoding ) , func ( t * testing . T ) {
s , err := encoding . NewEncoder ( ) . String ( input )
require . NoError ( t , err )
actual , err := ParseFile ( strings . NewReader ( s ) )
require . NoError ( t , err )
assert . Equal ( t , expect , actual . Commands )
} )
}
2024-05-20 18:26:45 +00:00
}