unipdf/ps/parser_test.go
2019-05-16 20:44:51 +00:00

424 lines
12 KiB
Go

/*
* This file is subject to the terms and conditions defined in
* file 'LICENSE.md', which is part of this source code package.
*/
package ps
import (
"errors"
"fmt"
"math"
"testing"
"github.com/unidoc/unipdf/v3/common"
)
func init() {
common.SetLogger(common.NewConsoleLogger(common.LogLevelDebug))
//common.SetLogger(common.NewConsoleLogger(common.LogLevelTrace))
}
func quickEval(progText string) (PSObject, error) {
parser := NewPSParser([]byte(progText))
prog, err := parser.Parse()
if err != nil {
return nil, err
}
fmt.Printf("%s\n", progText)
fmt.Printf("-> Program: %s\n", prog.DebugString())
exec := NewPSExecutor(prog)
outputs, err := exec.Execute(nil)
if err != nil {
return nil, err
}
if len(outputs) != 1 {
return nil, errors.New("stack result has too many values (>1)")
}
stack := PSStack(outputs)
fmt.Printf("=> Result Stack: %s\n", stack.DebugString())
return outputs[0], nil
}
func quickTest(progText string) (*PSStack, error) {
parser := NewPSParser([]byte(progText))
prog, err := parser.Parse()
if err != nil {
return nil, err
}
fmt.Printf("%s\n", progText)
fmt.Printf("-> Program: %s\n", prog.DebugString())
exec := NewPSExecutor(prog)
outputs, err := exec.Execute(nil)
if err != nil {
return nil, err
}
stack := PSStack(outputs)
return &stack, nil
}
func TestAdd1(t *testing.T) {
progText := "{ 1 1 add }"
obj, err := quickEval(progText)
if err != nil {
t.Errorf("Error: %v", err)
return
}
val, ok := obj.(*PSInteger)
if !ok {
t.Errorf("Wrong output type")
return
}
if val.Val != 2 {
t.Errorf("Wrong result")
return
}
}
func TestAdd2(t *testing.T) {
progText := "{ 1.1 1 add 3 4 add add }"
obj, err := quickEval(progText)
if err != nil {
t.Errorf("Error: %v", err)
return
}
val, ok := obj.(*PSReal)
if !ok {
t.Errorf("Wrong output type")
return
}
if math.Abs(val.Val-9.1) > tolerance {
t.Errorf("Wrong result")
return
}
}
//// 8.3 6.6 sub -> 1.7 (real)
// 8 6.3 sub -> 1.7 (real)
// 8 6 sub -> 2 (int)
func TestSub1(t *testing.T) {
progText := "{ 8.3 6.6 sub }"
obj, err := quickEval(progText)
if err != nil {
t.Errorf("Error: %v", err)
return
}
val, ok := obj.(*PSReal)
if !ok {
t.Errorf("Wrong output type")
return
}
if math.Abs(val.Val-1.7) > tolerance {
t.Errorf("Wrong result")
return
}
}
func TestSub2(t *testing.T) {
progText := "{ 8 6.3 sub }"
obj, err := quickEval(progText)
if err != nil {
t.Errorf("Error: %v", err)
return
}
val, ok := obj.(*PSReal)
if !ok {
t.Errorf("Wrong output type")
return
}
if math.Abs(val.Val-1.7) > tolerance {
t.Errorf("Wrong result")
return
}
}
func TestSub3(t *testing.T) {
progText := "{ 8 6 sub }"
obj, err := quickEval(progText)
if err != nil {
t.Errorf("Error: %v", err)
return
}
val, ok := obj.(*PSInteger)
if !ok {
t.Errorf("Wrong output type")
return
}
if val.Val != 2 {
t.Errorf("Wrong result")
return
}
}
// 6 + (3/8) -> 6.375
// 3 8 div 6 add
// 6 3 8 div add
//
// 8 - (7*3) -> -13
// 8 7 3 mul sub
// 7 3 mul 8 exch sub
// Simple test entry with a single expected PSObject output.
type SimpleTestEntry struct {
progText string
expected PSObject
}
func TestArithmetics(t *testing.T) {
testcases := []SimpleTestEntry{
{progText: "{ 3 8 div 6 add }", expected: MakeReal(6.375)},
{progText: "{ 6 3 8 div add }", expected: MakeReal(6.375)},
{progText: "{ 8 7 3 mul sub }", expected: MakeInteger(-13)},
{progText: "{ 7 3 mul 8 exch sub }", expected: MakeInteger(-13)},
}
for _, testcase := range testcases {
obj, err := quickEval(testcase.progText)
if err != nil {
t.Errorf("Error: %v", err)
return
}
// Maybe not the most robust test (comparing the strings), but should do.
if obj.DebugString() != testcase.expected.DebugString() {
t.Errorf("Wrong result: %s != %s", obj.DebugString(), testcase.expected.DebugString())
return
}
}
}
// Complex test entry can have a more complex output.
type ComplexTestEntry struct {
progText string
expected string
}
func TestStackOperations(t *testing.T) {
testcases := []ComplexTestEntry{
{progText: "{ 7 8 9 3 1 roll }", expected: "[ int:9 int:7 int:8 ]"},
{progText: "{ 7 8 9 3 -1 roll }", expected: "[ int:8 int:9 int:7 ]"},
{progText: "{ 9 7 8 3 -1 roll }", expected: "[ int:7 int:8 int:9 ]"},
{progText: "{ 1 1 0.2 7 8 9 3 1 roll }", expected: "[ int:1 int:1 real:0.20000 int:9 int:7 int:8 ]"},
}
for _, testcase := range testcases {
stack, err := quickTest(testcase.progText)
if err != nil {
t.Errorf("Error: %v", err)
return
}
// Maybe not the most robust test (comparing the strings), but should do.
if stack.DebugString() != testcase.expected {
t.Errorf("Wrong result: '%s' != '%s'", stack.DebugString(), testcase.expected)
return
}
}
}
func TestFunctionOperations(t *testing.T) {
testcases := []ComplexTestEntry{
// atan
{progText: "{ 0 1 atan }", expected: "[ real:0.00000 ]"},
{progText: "{ 1 0 atan }", expected: "[ real:90.00000 ]"},
{progText: "{ -100 0 atan }", expected: "[ real:270.00000 ]"},
{progText: "{ 4 4 atan }", expected: "[ real:45.00000 ]"},
}
for _, testcase := range testcases {
stack, err := quickTest(testcase.progText)
if err != nil {
t.Errorf("Error: %v", err)
return
}
// Maybe not the most robust test (comparing the strings), but should do.
if stack.DebugString() != testcase.expected {
t.Errorf("Wrong result: '%s' != '%s'", stack.DebugString(), testcase.expected)
return
}
}
}
func TestVariousCases(t *testing.T) {
testcases := []ComplexTestEntry{
// dup
{progText: "{ 99 dup }", expected: "[ int:99 int:99 ]"},
// ceiling
{progText: "{ 3.2 ceiling }", expected: "[ real:4.00000 ]"},
{progText: "{ -4.8 ceiling }", expected: "[ real:-4.00000 ]"},
{progText: "{ 99 ceiling }", expected: "[ int:99 ]"},
// floor
{progText: "{ 3.2 floor }", expected: "[ real:3.00000 ]"},
{progText: "{ -4.8 floor }", expected: "[ real:-5.00000 ]"},
{progText: "{ 99 floor }", expected: "[ int:99 ]"},
// exp
{progText: "{ 9 0.5 exp }", expected: "[ real:3.00000 ]"},
{progText: "{ -9 -1 exp }", expected: "[ real:-0.11111 ]"},
// and
{progText: "{ true true and }", expected: "[ bool:true ]"},
{progText: "{ true false and }", expected: "[ bool:false ]"},
{progText: "{ false true and }", expected: "[ bool:false ]"},
{progText: "{ false false and }", expected: "[ bool:false ]"},
{progText: "{ 99 1 and }", expected: "[ int:1 ]"},
{progText: "{ 52 7 and }", expected: "[ int:4 ]"},
// bitshift
{progText: "{ 7 3 bitshift }", expected: "[ int:56 ]"},
{progText: "{ 142 -3 bitshift }", expected: "[ int:17 ]"},
// copy
{progText: "{ 7 3 2 copy }", expected: "[ int:7 int:3 int:7 int:3 ]"},
{progText: "{ 7 3 0 copy }", expected: "[ int:7 int:3 ]"},
// cos
{progText: "{ 0 cos }", expected: "[ real:1.00000 ]"},
{progText: "{ 90 cos }", expected: "[ real:0.00000 ]"},
// eq.
{progText: "{ 4.0 4 eq }", expected: "[ bool:true ]"},
{progText: "{ 4 4.0 eq }", expected: "[ bool:true ]"},
{progText: "{ 4.0 4.0 eq }", expected: "[ bool:true ]"},
{progText: "{ 4 4 eq }", expected: "[ bool:true ]"},
{progText: "{ -4 4 eq }", expected: "[ bool:false ]"},
{progText: "{ false false eq }", expected: "[ bool:true ]"},
{progText: "{ true false eq }", expected: "[ bool:false ]"},
{progText: "{ true 4 eq }", expected: "[ bool:false ]"},
// ge
{progText: "{ 4.2 4 ge }", expected: "[ bool:true ]"},
{progText: "{ 4 4 ge }", expected: "[ bool:true ]"},
{progText: "{ 3.9 4 ge }", expected: "[ bool:false ]"},
// gt
{progText: "{ 4.2 4 gt }", expected: "[ bool:true ]"},
{progText: "{ 4 4 gt }", expected: "[ bool:false ]"},
{progText: "{ 3.9 4 gt }", expected: "[ bool:false ]"},
// if
{progText: "{ 4.2 4 gt {5} if }", expected: "[ int:5 ]"},
{progText: "{ 4.2 4 gt {4.0 4.0 ge {3} if} if}", expected: "[ int:3 ]"},
{progText: "{ 4.0 4.0 gt {5} if }", expected: "[ ]"},
// ifelse
{progText: "{ 4.2 4 gt {5} {4} ifelse }", expected: "[ int:5 ]"},
{progText: "{ 3 4 gt {5} {4} ifelse }", expected: "[ int:4 ]"},
// index
{progText: "{ 0 1 2 3 4 5 2 index }", expected: "[ int:0 int:1 int:2 int:3 int:4 int:5 int:3 ]"},
{progText: "{ 9 8 7 2 index }", expected: "[ int:9 int:8 int:7 int:9 ]"},
// le
{progText: "{ 4.2 4 le }", expected: "[ bool:false ]"},
{progText: "{ 4 4 le }", expected: "[ bool:true ]"},
{progText: "{ 3.9 4 le }", expected: "[ bool:true ]"},
// ln
{progText: "{ 10 ln }", expected: "[ real:2.30259 ]"},
{progText: "{ 100 ln }", expected: "[ real:4.60517 ]"},
// log
{progText: "{ 10 log }", expected: "[ real:1.00000 ]"},
{progText: "{ 100 log }", expected: "[ real:2.00000 ]"},
// lt
{progText: "{ 4.2 4 lt }", expected: "[ bool:false ]"},
{progText: "{ 4 4 lt }", expected: "[ bool:false ]"},
{progText: "{ 3.9 4 lt }", expected: "[ bool:true ]"},
// ne
{progText: "{ 4.0 4 ne }", expected: "[ bool:false ]"},
{progText: "{ 4 4.0 ne }", expected: "[ bool:false ]"},
{progText: "{ 4.0 4.0 ne }", expected: "[ bool:false ]"},
{progText: "{ 4 4 ne }", expected: "[ bool:false ]"},
{progText: "{ -4 4 ne }", expected: "[ bool:true ]"},
{progText: "{ false false ne }", expected: "[ bool:false ]"},
{progText: "{ true false ne }", expected: "[ bool:true ]"},
{progText: "{ true 4 ne }", expected: "[ bool:true ]"},
// neg
// not
{progText: "{ true not }", expected: "[ bool:false ]"},
{progText: "{ false not }", expected: "[ bool:true ]"},
{progText: "{ 52 not }", expected: "[ int:-53 ]"},
// or
{progText: "{ true true or }", expected: "[ bool:true ]"},
{progText: "{ true false or }", expected: "[ bool:true ]"},
{progText: "{ false true or }", expected: "[ bool:true ]"},
{progText: "{ false false or }", expected: "[ bool:false ]"},
{progText: "{ 17 5 or }", expected: "[ int:21 ]"},
// pop
{progText: "{ 1 2 3 pop }", expected: "[ int:1 int:2 ]"},
{progText: "{ 1 2 pop }", expected: "[ int:1 ]"},
{progText: "{ 1 pop }", expected: "[ ]"},
// round
{progText: "{ 3.2 round }", expected: "[ real:3.00000 ]"},
{progText: "{ 6.5 round }", expected: "[ real:7.00000 ]"},
{progText: "{ -4.8 round }", expected: "[ real:-5.00000 ]"},
{progText: "{ -6.5 round }", expected: "[ real:-6.00000 ]"},
{progText: "{ 99 round }", expected: "[ int:99 ]"},
// roll
{progText: "{ 1 2 3 3 -1 roll }", expected: "[ int:2 int:3 int:1 ]"},
{progText: "{ 1 2 3 3 1 roll }", expected: "[ int:3 int:1 int:2 ]"},
{progText: "{ 1 2 3 3 0 roll }", expected: "[ int:1 int:2 int:3 ]"},
// sin
{progText: "{ 0 sin }", expected: "[ real:0.00000 ]"},
{progText: "{ 90 sin }", expected: "[ real:1.00000 ]"},
// sqrt
{progText: "{ 4 sqrt }", expected: "[ real:2.00000 ]"},
{progText: "{ 2 sqrt }", expected: "[ real:1.41421 ]"},
// truncate
{progText: "{ 3.2 truncate }", expected: "[ real:3.00000 ]"},
{progText: "{ -4.8 truncate }", expected: "[ real:-4.00000 ]"},
{progText: "{ 99 truncate }", expected: "[ int:99 ]"},
// xor
{progText: "{ true true xor }", expected: "[ bool:false ]"},
{progText: "{ true false xor }", expected: "[ bool:true ]"},
{progText: "{ false true xor }", expected: "[ bool:true ]"},
{progText: "{ false false xor }", expected: "[ bool:false ]"},
{progText: "{ 7 3 xor }", expected: "[ int:4 ]"},
{progText: "{ 12 3 xor }", expected: "[ int:15 ]"},
}
for _, testcase := range testcases {
stack, err := quickTest(testcase.progText)
if err != nil {
t.Errorf("Error: %v", err)
return
}
// Maybe not the most robust test (comparing the strings), but should do.
if stack.DebugString() != testcase.expected {
t.Errorf("Wrong result: '%s' != '%s'", stack.DebugString(), testcase.expected)
return
}
}
}
func TestTintTransform1(t *testing.T) {
testcases := []ComplexTestEntry{
// from corpus epson_pages3_color_pages1.pdf.
{progText: "{ 0.0000 dup 0 mul exch dup 0 mul exch dup 0 mul exch 1 mul }", expected: "[ real:0.00000 real:0.00000 real:0.00000 real:0.00000 ]"},
}
for _, testcase := range testcases {
stack, err := quickTest(testcase.progText)
if err != nil {
t.Errorf("Error: %v", err)
return
}
// Maybe not the most robust test (comparing the strings), but should do.
if stack.DebugString() != testcase.expected {
t.Errorf("Wrong result: '%s' != '%s'", stack.DebugString(), testcase.expected)
return
}
}
}