mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-17 12:42:16 +01:00
fix: LibreOffice newer versions stability (#697)
This commit is contained in:
@@ -8,8 +8,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gotenberg/gotenberg/v7/pkg/gotenberg"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gotenberg/gotenberg/v7/pkg/gotenberg"
|
||||
)
|
||||
|
||||
type listener interface {
|
||||
@@ -42,15 +43,18 @@ type libreOfficeListener struct {
|
||||
queueLength int
|
||||
queueLengthMu sync.RWMutex
|
||||
lockChan chan struct{}
|
||||
logger *zap.Logger
|
||||
|
||||
fs *gotenberg.FileSystem
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func newLibreOfficeListener(logger *zap.Logger, binPath string, startTimeout time.Duration, threshold int) listener {
|
||||
func newLibreOfficeListener(logger *zap.Logger, fs *gotenberg.FileSystem, binPath string, startTimeout time.Duration, threshold int) listener {
|
||||
return &libreOfficeListener{
|
||||
binPath: binPath,
|
||||
startTimeout: startTimeout,
|
||||
threshold: threshold,
|
||||
lockChan: make(chan struct{}, 1),
|
||||
fs: fs,
|
||||
logger: logger.Named("listener"),
|
||||
}
|
||||
}
|
||||
@@ -65,10 +69,7 @@ func (listener *libreOfficeListener) start(logger *zap.Logger) error {
|
||||
return fmt.Errorf("get free port: %w", err)
|
||||
}
|
||||
|
||||
// Good to know: the garbage collector might delete the next directory
|
||||
// while it is still running. It does seem to cause any issue though.
|
||||
userProfileDirPath := gotenberg.NewDirPath()
|
||||
|
||||
userProfileDirPath := listener.fs.NewDirPath()
|
||||
args := []string{
|
||||
"--headless",
|
||||
"--invisible",
|
||||
@@ -158,6 +159,12 @@ func (listener *libreOfficeListener) start(logger *zap.Logger) error {
|
||||
if err != nil {
|
||||
logger.Debug(fmt.Sprintf("kill LibreOffice listener process: %v", err))
|
||||
}
|
||||
|
||||
// And the user profile directory is deleted.
|
||||
err = os.RemoveAll(userProfileDirPath)
|
||||
if err != nil {
|
||||
logger.Debug(fmt.Sprintf("remove user profile directory: %v", err))
|
||||
}
|
||||
}()
|
||||
|
||||
logger.Debug("waiting for the LibreOffice listener socket to be available...")
|
||||
@@ -185,9 +192,21 @@ func (listener *libreOfficeListener) stop(logger *zap.Logger) error {
|
||||
defer func() {
|
||||
defer listener.cfgMu.RUnlock()
|
||||
|
||||
if listener.userProfileDirPath == "" {
|
||||
return
|
||||
}
|
||||
|
||||
err := os.RemoveAll(listener.userProfileDirPath)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("remove LibreOffice listener user profile directory: %v", err))
|
||||
logger.Error(fmt.Sprintf("remove LibreOffice listener's user profile directory: %v", err))
|
||||
}
|
||||
|
||||
logger.Debug(fmt.Sprintf("'%s' LibreOffice listener's user profile directory removed", listener.userProfileDirPath))
|
||||
|
||||
// Also remove listener specific files in the temporary directory.
|
||||
err = gotenberg.GarbageCollect(logger, os.TempDir(), []string{"OSL_PIPE", ".tmp"})
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -317,7 +336,7 @@ func (listener *libreOfficeListener) unlock(logger *zap.Logger) error {
|
||||
}
|
||||
|
||||
listener.usage += 1
|
||||
if listener.usage < listener.threshold {
|
||||
if listener.threshold > 0 && listener.usage < listener.threshold {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gotenberg/gotenberg/v7/pkg/gotenberg"
|
||||
)
|
||||
|
||||
func TestListener_start(t *testing.T) {
|
||||
@@ -17,11 +19,11 @@ func TestListener_start(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "nominal behavior",
|
||||
listener: newLibreOfficeListener(zap.NewNop(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10),
|
||||
listener: newLibreOfficeListener(zap.NewNop(), gotenberg.NewFileSystem(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10),
|
||||
},
|
||||
{
|
||||
name: "non-exit code 81 on first start",
|
||||
listener: newLibreOfficeListener(zap.NewNop(), "foo", time.Duration(10)*time.Second, 10),
|
||||
listener: newLibreOfficeListener(zap.NewNop(), gotenberg.NewFileSystem(), "foo", time.Duration(10)*time.Second, 10),
|
||||
expectStartErr: true,
|
||||
},
|
||||
}
|
||||
@@ -57,6 +59,7 @@ func TestListener_start(t *testing.T) {
|
||||
func TestListener_stop(t *testing.T) {
|
||||
listener := newLibreOfficeListener(
|
||||
zap.NewNop(),
|
||||
gotenberg.NewFileSystem(),
|
||||
os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
time.Duration(10)*time.Second,
|
||||
10,
|
||||
@@ -76,6 +79,7 @@ func TestListener_stop(t *testing.T) {
|
||||
func TestListener_restart(t *testing.T) {
|
||||
listener := newLibreOfficeListener(
|
||||
zap.NewNop(),
|
||||
gotenberg.NewFileSystem(),
|
||||
os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
time.Duration(10)*time.Second,
|
||||
10,
|
||||
@@ -107,7 +111,7 @@ func TestListener_lock(t *testing.T) {
|
||||
{
|
||||
name: "nominal behavior",
|
||||
listener: func() listener {
|
||||
listener := newLibreOfficeListener(zap.NewNop(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10)
|
||||
listener := newLibreOfficeListener(zap.NewNop(), gotenberg.NewFileSystem(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10)
|
||||
|
||||
err := listener.start(zap.NewNop())
|
||||
if err != nil {
|
||||
@@ -123,7 +127,7 @@ func TestListener_lock(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "first start",
|
||||
listener: newLibreOfficeListener(zap.NewNop(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10),
|
||||
listener: newLibreOfficeListener(zap.NewNop(), gotenberg.NewFileSystem(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10),
|
||||
ctx: context.Background(),
|
||||
teardown: func(listener listener) error {
|
||||
return listener.stop(zap.NewNop())
|
||||
@@ -132,7 +136,7 @@ func TestListener_lock(t *testing.T) {
|
||||
{
|
||||
name: "unhealthy listener",
|
||||
listener: func() listener {
|
||||
listener := newLibreOfficeListener(zap.NewNop(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10)
|
||||
listener := newLibreOfficeListener(zap.NewNop(), gotenberg.NewFileSystem(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10)
|
||||
|
||||
err := listener.start(zap.NewNop())
|
||||
if err != nil {
|
||||
@@ -154,7 +158,7 @@ func TestListener_lock(t *testing.T) {
|
||||
{
|
||||
name: "context done",
|
||||
listener: func() listener {
|
||||
listener := newLibreOfficeListener(zap.NewNop(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10)
|
||||
listener := newLibreOfficeListener(zap.NewNop(), gotenberg.NewFileSystem(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10)
|
||||
|
||||
err := listener.start(zap.NewNop())
|
||||
if err != nil {
|
||||
@@ -212,7 +216,7 @@ func TestListener_unlock(t *testing.T) {
|
||||
{
|
||||
name: "nominal behavior",
|
||||
listener: func() listener {
|
||||
listener := newLibreOfficeListener(zap.NewNop(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10)
|
||||
listener := newLibreOfficeListener(zap.NewNop(), gotenberg.NewFileSystem(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10)
|
||||
|
||||
err := listener.start(zap.NewNop())
|
||||
if err != nil {
|
||||
@@ -233,7 +237,7 @@ func TestListener_unlock(t *testing.T) {
|
||||
{
|
||||
name: "unhealthy listener",
|
||||
listener: func() listener {
|
||||
listener := newLibreOfficeListener(zap.NewNop(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10)
|
||||
listener := newLibreOfficeListener(zap.NewNop(), gotenberg.NewFileSystem(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10)
|
||||
|
||||
err := listener.start(zap.NewNop())
|
||||
if err != nil {
|
||||
@@ -259,7 +263,7 @@ func TestListener_unlock(t *testing.T) {
|
||||
{
|
||||
name: "threshold reached",
|
||||
listener: func() listener {
|
||||
listener := newLibreOfficeListener(zap.NewNop(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 1)
|
||||
listener := newLibreOfficeListener(zap.NewNop(), gotenberg.NewFileSystem(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 1)
|
||||
|
||||
err := listener.start(zap.NewNop())
|
||||
if err != nil {
|
||||
@@ -299,6 +303,7 @@ func TestListener_unlock(t *testing.T) {
|
||||
func TestListener_port(t *testing.T) {
|
||||
listener := newLibreOfficeListener(
|
||||
zap.NewNop(),
|
||||
gotenberg.NewFileSystem(),
|
||||
os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
time.Duration(10)*time.Second,
|
||||
10,
|
||||
@@ -323,6 +328,7 @@ func TestListener_port(t *testing.T) {
|
||||
func TestListener_queue(t *testing.T) {
|
||||
listener := newLibreOfficeListener(
|
||||
zap.NewNop(),
|
||||
gotenberg.NewFileSystem(),
|
||||
os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
time.Duration(10)*time.Second,
|
||||
10,
|
||||
@@ -395,6 +401,7 @@ func TestListener_healthy(t *testing.T) {
|
||||
startTimeout: time.Duration(10) * time.Second,
|
||||
threshold: 10,
|
||||
lockChan: make(chan struct{}, 1),
|
||||
fs: gotenberg.NewFileSystem(),
|
||||
logger: zap.NewNop(),
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/alexliesenfeld/health"
|
||||
"github.com/gotenberg/gotenberg/v7/pkg/gotenberg"
|
||||
"github.com/gotenberg/gotenberg/v7/pkg/modules/api"
|
||||
flag "github.com/spf13/pflag"
|
||||
"go.uber.org/multierr"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gotenberg/gotenberg/v7/pkg/gotenberg"
|
||||
"github.com/gotenberg/gotenberg/v7/pkg/modules/api"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -82,10 +83,10 @@ func (UNO) Descriptor() gotenberg.ModuleDescriptor {
|
||||
FlagSet: func() *flag.FlagSet {
|
||||
fs := flag.NewFlagSet("uno", flag.ExitOnError)
|
||||
fs.Duration("uno-listener-start-timeout", time.Duration(10)*time.Second, "Time limit for restarting the LibreOffice listener")
|
||||
fs.Int("uno-listener-restart-threshold", 10, "Conversions limit after which the LibreOffice listener is restarted - 0 means no long-running LibreOffice listener")
|
||||
fs.Int("uno-listener-restart-threshold", 10, "Conversions limit after which the LibreOffice listener is restarted - 0 means no restart")
|
||||
fs.Bool("unoconv-disable-listener", false, "Do not start a long-running listener - save resources in detriment of unitary performance")
|
||||
|
||||
err := fs.MarkDeprecated("unoconv-disable-listener", "use uno-listener-restart-threshold with 0 instead")
|
||||
err := fs.MarkDeprecated("unoconv-disable-listener", "listener cannot be disabled")
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("create deprecated flags for the uno module: %v", err))
|
||||
}
|
||||
@@ -134,8 +135,10 @@ func (mod *UNO) Provision(ctx *gotenberg.Context) error {
|
||||
|
||||
mod.logger = logger
|
||||
|
||||
// Listener.
|
||||
mod.listener = newLibreOfficeListener(
|
||||
mod.logger,
|
||||
gotenberg.NewFileSystem(),
|
||||
mod.libreOfficeBinPath,
|
||||
mod.libreOfficeStartTimeout,
|
||||
mod.libreOfficeRestartThreshold,
|
||||
@@ -170,19 +173,11 @@ func (mod UNO) Start() error {
|
||||
|
||||
// StartupMessage returns a custom startup message.
|
||||
func (mod UNO) StartupMessage() string {
|
||||
if mod.libreOfficeRestartThreshold == 0 {
|
||||
return "long-running LibreOffice listener disabled"
|
||||
}
|
||||
|
||||
return "long-running LibreOffice listener ready to start"
|
||||
}
|
||||
|
||||
// Stop stops the long-running LibreOffice Listener if it exists.
|
||||
// Stop stops the long-running LibreOffice Listener.
|
||||
func (mod UNO) Stop(ctx context.Context) error {
|
||||
if mod.libreOfficeRestartThreshold == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Block until the context is done so that other module may gracefully stop
|
||||
// before we do a shutdown cleanup.
|
||||
mod.logger.Debug("wait for the end of grace duration")
|
||||
@@ -194,7 +189,7 @@ func (mod UNO) Stop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("stop long-running LibreOffice listener")
|
||||
return fmt.Errorf("stop long-running LibreOffice listener: %w", err)
|
||||
}
|
||||
|
||||
// Metrics returns the metrics.
|
||||
@@ -284,14 +279,8 @@ func (mod UNO) Checks() ([]health.CheckerOption, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PDF converts a document to PDF.
|
||||
//
|
||||
// If there is no long-running LibreOffice listener, it creates a dedicated
|
||||
// LibreOffice instance for the conversion. Substantial calls to this method
|
||||
// may increase CPU and memory usage drastically
|
||||
//
|
||||
// If there is a long-running LibreOffice listener, the conversion performance
|
||||
// improves substantially. However, it cannot perform parallel operations.
|
||||
// PDF converts a document to PDF. Be cautious when making multiple concurrent
|
||||
// calls, as it might lead to reaching the context's deadline.
|
||||
func (mod UNO) PDF(ctx context.Context, logger *zap.Logger, inputPath, outputPath string, options Options) error {
|
||||
args := []string{
|
||||
"--no-launch",
|
||||
@@ -299,45 +288,26 @@ func (mod UNO) PDF(ctx context.Context, logger *zap.Logger, inputPath, outputPat
|
||||
"pdf",
|
||||
}
|
||||
|
||||
switch mod.libreOfficeRestartThreshold {
|
||||
case 0:
|
||||
listener := newLibreOfficeListener(logger, mod.libreOfficeBinPath, mod.libreOfficeStartTimeout, 0)
|
||||
err := mod.listener.lock(ctx, logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("lock long-running LibreOffice listener: %w", err)
|
||||
}
|
||||
|
||||
err := listener.start(logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("start LibreOffice listener: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := listener.stop(logger)
|
||||
defer func() {
|
||||
go func() {
|
||||
err := mod.listener.unlock(logger)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("stop LibreOffice listener: %v", err))
|
||||
mod.logger.Error(fmt.Sprintf("unlock long-running LibreOffice listener: %v", err))
|
||||
|
||||
return
|
||||
}
|
||||
}()
|
||||
}()
|
||||
|
||||
args = append(args, "--port", fmt.Sprintf("%d", listener.port()))
|
||||
default:
|
||||
err := mod.listener.lock(ctx, logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("lock long-running LibreOffice listener: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
go func() {
|
||||
err := mod.listener.unlock(logger)
|
||||
if err != nil {
|
||||
mod.logger.Error(fmt.Sprintf("unlock long-running LibreOffice listener: %v", err))
|
||||
|
||||
return
|
||||
}
|
||||
}()
|
||||
}()
|
||||
|
||||
// If the LibreOffice listener is restarting while acquiring the lock,
|
||||
// the port will change. It's therefore important to add the port args
|
||||
// after we acquire the lock.
|
||||
args = append(args, "--port", fmt.Sprintf("%d", mod.listener.port()))
|
||||
}
|
||||
// If the LibreOffice listener is restarting while acquiring the lock,
|
||||
// the port will change. It's therefore important to add the port args
|
||||
// after we acquire the lock.
|
||||
args = append(args, "--port", fmt.Sprintf("%d", mod.listener.port()))
|
||||
|
||||
checkedEntry := logger.Check(zap.DebugLevel, "check for debug level before setting high verbosity")
|
||||
if checkedEntry != nil {
|
||||
|
||||
@@ -9,9 +9,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/alexliesenfeld/health"
|
||||
"github.com/gotenberg/gotenberg/v7/pkg/gotenberg"
|
||||
flag "github.com/spf13/pflag"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/gotenberg/gotenberg/v7/pkg/gotenberg"
|
||||
)
|
||||
|
||||
func TestUNO_Descriptor(t *testing.T) {
|
||||
@@ -78,7 +79,6 @@ func TestUNO_Provision(t *testing.T) {
|
||||
FlagSet: func() *flag.FlagSet {
|
||||
fs := new(UNO).Descriptor().FlagSet
|
||||
err := fs.Parse([]string{"--unoconv-disable-listener=true"})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error from fs.Parse(), but got: %v", err)
|
||||
}
|
||||
@@ -205,35 +205,11 @@ func TestUNO_Start(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUNO_StartupMessage(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
mod UNO
|
||||
expectMessage string
|
||||
}{
|
||||
{
|
||||
name: "long-running LibreOffice listener ready to start",
|
||||
mod: UNO{
|
||||
libreOfficeRestartThreshold: 10,
|
||||
},
|
||||
expectMessage: "long-running LibreOffice listener ready to start",
|
||||
},
|
||||
{
|
||||
name: "long-running LibreOffice listener disabled",
|
||||
mod: UNO{
|
||||
libreOfficeRestartThreshold: 0,
|
||||
},
|
||||
expectMessage: "long-running LibreOffice listener disabled",
|
||||
},
|
||||
}
|
||||
actual := new(UNO).StartupMessage()
|
||||
expect := "long-running LibreOffice listener ready to start"
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
actual := tc.mod.StartupMessage()
|
||||
|
||||
if tc.expectMessage != actual {
|
||||
t.Errorf("expected '%s' from mod.StartupMessage(), but got '%s'", tc.expectMessage, actual)
|
||||
}
|
||||
})
|
||||
if actual != expect {
|
||||
t.Errorf("expected '%s' but got '%s'", expect, actual)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,13 +231,6 @@ func TestUNO_Stop(t *testing.T) {
|
||||
logger: zap.NewNop(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no long-running LibreOffice listener",
|
||||
mod: UNO{
|
||||
libreOfficeRestartThreshold: 0,
|
||||
logger: zap.NewNop(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "stop error",
|
||||
mod: UNO{
|
||||
@@ -468,7 +437,7 @@ func TestUNO_Checks(t *testing.T) {
|
||||
|
||||
func TestUNO_PDF(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
scenario string
|
||||
mod UNO
|
||||
ctx context.Context
|
||||
logger *zap.Logger
|
||||
@@ -478,19 +447,7 @@ func TestUNO_PDF(t *testing.T) {
|
||||
teardown func(mod UNO) error
|
||||
}{
|
||||
{
|
||||
name: "nominal behavior with no long-running LibreOffice listener",
|
||||
mod: UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 0,
|
||||
},
|
||||
ctx: context.Background(),
|
||||
logger: zap.NewNop(),
|
||||
inputPath: "/tests/test/testdata/libreoffice/sample1.docx",
|
||||
},
|
||||
{
|
||||
name: "nominal behavior with a long-running LibreOffice listener",
|
||||
scenario: "nominal behavior with a long-running LibreOffice listener",
|
||||
mod: func() UNO {
|
||||
mod := UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
@@ -501,6 +458,7 @@ func TestUNO_PDF(t *testing.T) {
|
||||
}
|
||||
mod.listener = newLibreOfficeListener(
|
||||
mod.logger,
|
||||
gotenberg.NewFileSystem(),
|
||||
mod.libreOfficeBinPath,
|
||||
mod.libreOfficeStartTimeout,
|
||||
mod.libreOfficeRestartThreshold,
|
||||
@@ -518,56 +476,122 @@ func TestUNO_PDF(t *testing.T) {
|
||||
return mod.Stop(ctx)
|
||||
},
|
||||
},
|
||||
//{
|
||||
// scenario: "convert with a debug logger",
|
||||
// mod: func() UNO {
|
||||
// mod := UNO{
|
||||
// unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
// libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
// libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
// libreOfficeRestartThreshold: 10,
|
||||
// logger: zap.NewNop(),
|
||||
// }
|
||||
// mod.listener = newLibreOfficeListener(
|
||||
// mod.logger,
|
||||
// gotenberg.NewFileSystem(),
|
||||
// mod.libreOfficeBinPath,
|
||||
// mod.libreOfficeStartTimeout,
|
||||
// mod.libreOfficeRestartThreshold,
|
||||
// )
|
||||
//
|
||||
// return mod
|
||||
// }(),
|
||||
// ctx: context.Background(),
|
||||
// logger: zap.NewExample(),
|
||||
// inputPath: "/tests/test/testdata/libreoffice/sample1.docx",
|
||||
// teardown: func(mod UNO) error {
|
||||
// ctx, cancel := context.WithCancel(context.Background())
|
||||
// cancel()
|
||||
//
|
||||
// return mod.Stop(ctx)
|
||||
// },
|
||||
//},
|
||||
{
|
||||
name: "convert with a debug logger",
|
||||
mod: UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 0,
|
||||
},
|
||||
ctx: context.Background(),
|
||||
logger: zap.NewExample(),
|
||||
inputPath: "/tests/test/testdata/libreoffice/sample1.docx",
|
||||
},
|
||||
{
|
||||
name: "convert with landscape",
|
||||
mod: UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 0,
|
||||
},
|
||||
scenario: "convert with landscape",
|
||||
mod: func() UNO {
|
||||
mod := UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 10,
|
||||
logger: zap.NewNop(),
|
||||
}
|
||||
mod.listener = newLibreOfficeListener(
|
||||
mod.logger,
|
||||
gotenberg.NewFileSystem(),
|
||||
mod.libreOfficeBinPath,
|
||||
mod.libreOfficeStartTimeout,
|
||||
mod.libreOfficeRestartThreshold,
|
||||
)
|
||||
|
||||
return mod
|
||||
}(),
|
||||
ctx: context.Background(),
|
||||
logger: zap.NewNop(),
|
||||
inputPath: "/tests/test/testdata/libreoffice/sample1.docx",
|
||||
options: Options{
|
||||
Landscape: true,
|
||||
},
|
||||
teardown: func(mod UNO) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
return mod.Stop(ctx)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "convert with page ranges",
|
||||
mod: UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 0,
|
||||
},
|
||||
scenario: "convert with page ranges",
|
||||
mod: func() UNO {
|
||||
mod := UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 10,
|
||||
logger: zap.NewNop(),
|
||||
}
|
||||
mod.listener = newLibreOfficeListener(
|
||||
mod.logger,
|
||||
gotenberg.NewFileSystem(),
|
||||
mod.libreOfficeBinPath,
|
||||
mod.libreOfficeStartTimeout,
|
||||
mod.libreOfficeRestartThreshold,
|
||||
)
|
||||
|
||||
return mod
|
||||
}(),
|
||||
ctx: context.Background(),
|
||||
logger: zap.NewNop(),
|
||||
inputPath: "/tests/test/testdata/libreoffice/sample1.docx",
|
||||
options: Options{
|
||||
PageRanges: "1-2",
|
||||
},
|
||||
teardown: func(mod UNO) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
return mod.Stop(ctx)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "convert with invalid page ranges",
|
||||
mod: UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 0,
|
||||
},
|
||||
scenario: "convert with invalid page ranges",
|
||||
mod: func() UNO {
|
||||
mod := UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 10,
|
||||
logger: zap.NewNop(),
|
||||
}
|
||||
mod.listener = newLibreOfficeListener(
|
||||
mod.logger,
|
||||
gotenberg.NewFileSystem(),
|
||||
mod.libreOfficeBinPath,
|
||||
mod.libreOfficeStartTimeout,
|
||||
mod.libreOfficeRestartThreshold,
|
||||
)
|
||||
|
||||
return mod
|
||||
}(),
|
||||
ctx: context.Background(),
|
||||
logger: zap.NewNop(),
|
||||
inputPath: "/tests/test/testdata/libreoffice/sample1.docx",
|
||||
@@ -575,60 +599,132 @@ func TestUNO_PDF(t *testing.T) {
|
||||
PageRanges: "foo",
|
||||
},
|
||||
expectPDFErr: true,
|
||||
teardown: func(mod UNO) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
return mod.Stop(ctx)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "convert to PDF/A-1a",
|
||||
mod: UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 0,
|
||||
},
|
||||
scenario: "convert to PDF/A-1a",
|
||||
mod: func() UNO {
|
||||
mod := UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 10,
|
||||
logger: zap.NewNop(),
|
||||
}
|
||||
mod.listener = newLibreOfficeListener(
|
||||
mod.logger,
|
||||
gotenberg.NewFileSystem(),
|
||||
mod.libreOfficeBinPath,
|
||||
mod.libreOfficeStartTimeout,
|
||||
mod.libreOfficeRestartThreshold,
|
||||
)
|
||||
|
||||
return mod
|
||||
}(),
|
||||
ctx: context.Background(),
|
||||
logger: zap.NewNop(),
|
||||
inputPath: "/tests/test/testdata/libreoffice/sample1.docx",
|
||||
options: Options{
|
||||
PDFformat: gotenberg.FormatPDFA1a,
|
||||
},
|
||||
teardown: func(mod UNO) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
return mod.Stop(ctx)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "convert to PDF/A-2b",
|
||||
mod: UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 0,
|
||||
},
|
||||
scenario: "convert to PDF/A-2b",
|
||||
mod: func() UNO {
|
||||
mod := UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 10,
|
||||
logger: zap.NewNop(),
|
||||
}
|
||||
mod.listener = newLibreOfficeListener(
|
||||
mod.logger,
|
||||
gotenberg.NewFileSystem(),
|
||||
mod.libreOfficeBinPath,
|
||||
mod.libreOfficeStartTimeout,
|
||||
mod.libreOfficeRestartThreshold,
|
||||
)
|
||||
|
||||
return mod
|
||||
}(),
|
||||
ctx: context.Background(),
|
||||
logger: zap.NewNop(),
|
||||
inputPath: "/tests/test/testdata/libreoffice/sample1.docx",
|
||||
options: Options{
|
||||
PDFformat: gotenberg.FormatPDFA2b,
|
||||
},
|
||||
teardown: func(mod UNO) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
return mod.Stop(ctx)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "convert to PDF/A-3b",
|
||||
mod: UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 0,
|
||||
},
|
||||
scenario: "convert to PDF/A-3b",
|
||||
mod: func() UNO {
|
||||
mod := UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 10,
|
||||
logger: zap.NewNop(),
|
||||
}
|
||||
mod.listener = newLibreOfficeListener(
|
||||
mod.logger,
|
||||
gotenberg.NewFileSystem(),
|
||||
mod.libreOfficeBinPath,
|
||||
mod.libreOfficeStartTimeout,
|
||||
mod.libreOfficeRestartThreshold,
|
||||
)
|
||||
|
||||
return mod
|
||||
}(),
|
||||
ctx: context.Background(),
|
||||
logger: zap.NewNop(),
|
||||
inputPath: "/tests/test/testdata/libreoffice/sample1.docx",
|
||||
options: Options{
|
||||
PDFformat: gotenberg.FormatPDFA3b,
|
||||
},
|
||||
teardown: func(mod UNO) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
return mod.Stop(ctx)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "convert to invalid PDF format",
|
||||
mod: UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 0,
|
||||
},
|
||||
scenario: "convert to invalid PDF format",
|
||||
mod: func() UNO {
|
||||
mod := UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 10,
|
||||
logger: zap.NewNop(),
|
||||
}
|
||||
mod.listener = newLibreOfficeListener(
|
||||
mod.logger,
|
||||
gotenberg.NewFileSystem(),
|
||||
mod.libreOfficeBinPath,
|
||||
mod.libreOfficeStartTimeout,
|
||||
mod.libreOfficeRestartThreshold,
|
||||
)
|
||||
|
||||
return mod
|
||||
}(),
|
||||
ctx: context.Background(),
|
||||
logger: zap.NewNop(),
|
||||
inputPath: "/tests/test/testdata/libreoffice/sample1.docx",
|
||||
@@ -636,28 +732,33 @@ func TestUNO_PDF(t *testing.T) {
|
||||
PDFformat: "foo",
|
||||
},
|
||||
expectPDFErr: true,
|
||||
teardown: func(mod UNO) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
return mod.Stop(ctx)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil context",
|
||||
mod: UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 0,
|
||||
},
|
||||
ctx: nil,
|
||||
logger: zap.NewNop(),
|
||||
inputPath: "/tests/test/testdata/libreoffice/sample1.docx",
|
||||
expectPDFErr: true,
|
||||
},
|
||||
{
|
||||
name: "expired context",
|
||||
mod: UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 0,
|
||||
},
|
||||
scenario: "expired context",
|
||||
mod: func() UNO {
|
||||
mod := UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
libreOfficeStartTimeout: time.Duration(10) * time.Second,
|
||||
libreOfficeRestartThreshold: 10,
|
||||
logger: zap.NewNop(),
|
||||
}
|
||||
mod.listener = newLibreOfficeListener(
|
||||
mod.logger,
|
||||
gotenberg.NewFileSystem(),
|
||||
mod.libreOfficeBinPath,
|
||||
mod.libreOfficeStartTimeout,
|
||||
mod.libreOfficeRestartThreshold,
|
||||
)
|
||||
|
||||
return mod
|
||||
}(),
|
||||
ctx: func() context.Context {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
@@ -669,7 +770,7 @@ func TestUNO_PDF(t *testing.T) {
|
||||
expectPDFErr: true,
|
||||
},
|
||||
{
|
||||
name: "cannot lock long-running LibreOffice listener",
|
||||
scenario: "cannot lock long-running LibreOffice listener",
|
||||
mod: UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
@@ -688,7 +789,7 @@ func TestUNO_PDF(t *testing.T) {
|
||||
expectPDFErr: true,
|
||||
},
|
||||
{
|
||||
name: "cannot unlock long-running LibreOffice listener",
|
||||
scenario: "cannot unlock long-running LibreOffice listener",
|
||||
mod: UNO{
|
||||
unoconvBinPath: os.Getenv("UNOCONV_BIN_PATH"),
|
||||
libreOfficeBinPath: os.Getenv("LIBREOFFICE_BIN_PATH"),
|
||||
@@ -715,7 +816,7 @@ func TestUNO_PDF(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Run(tc.scenario, func(t *testing.T) {
|
||||
defer func() {
|
||||
if tc.teardown == nil {
|
||||
return
|
||||
@@ -727,15 +828,16 @@ func TestUNO_PDF(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
outputDir, err := gotenberg.MkdirAll()
|
||||
fs := gotenberg.NewFileSystem()
|
||||
outputDir, err := fs.MkdirAll()
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error from gotenberg.MkdirAll(), but got: %v", err)
|
||||
t.Fatalf("test %s: expected error but got: %v", tc.scenario, err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := os.RemoveAll(outputDir)
|
||||
err := os.RemoveAll(fs.WorkingDirPath())
|
||||
if err != nil {
|
||||
t.Errorf("expected no error from os.RemoveAll(), but got: %v", err)
|
||||
t.Fatalf("test %s: expected no error while cleaning up but got: %v", tc.scenario, err)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user