Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Ruby

on:
push:
pull_request:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ruby-version:
- '3.2'
- '3.3'
- '3.4'
- '4.0'

steps:
- uses: actions/checkout@v7
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true
- name: Run unit and specification tests
run: bundle exec rake test
- name: Check and schedule a project
run: |
bundle exec ruby -Ilib lib/tj3.rb --silent --check-syntax test/TestSuite/Syntax/Correct/Simple.tjp
bundle exec ruby -Ilib lib/tj3.rb --silent --no-reports test/TestSuite/Syntax/Correct/Simple.tjp
bundle exec ruby -Ilib lib/tj3.rb --silent --no-reports --list-reports '.*' test/TestSuite/Syntax/Correct/tutorial.tjp
- name: Generate reports with multiple workers
run: |
mkdir -p "${RUNNER_TEMP}/taskjuggler-reports"
bundle exec ruby -Ilib lib/tj3.rb --silent -c 2 \
--output-dir "${RUNNER_TEMP}/taskjuggler-reports" \
test/TestSuite/Syntax/Correct/tutorial.tjp
test "$(find "${RUNNER_TEMP}/taskjuggler-reports" -type f | wc -l)" -eq 20
- name: Exercise the daemon and web server
run: bundle exec ruby test/web_server_smoke.rb
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
benchmarks/css
benchmarks/icons
benchmarks/scripts
/Gemfile.lock
CHANGELOG
doc
lib/css
Expand Down
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
source 'https://rubygems.org'

gemspec
2 changes: 1 addition & 1 deletion lib/taskjuggler/BatchProcessor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def receiver
# Remove the job from the @runningJobs Hash.
@runningJobs.delete(pid)
# Save the return value.
job.retVal = retVal.exitstatus
job.retVal = retVal
if retVal.signaled?
cleanPipes(job)
# Aborted jobs will probably not send an EOT. So we fastrack
Expand Down
2 changes: 1 addition & 1 deletion lib/taskjuggler/daemon/Daemon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def start

# We no longer have a controlling terminal, so these are useless.
$stdin.reopen('/dev/null')
$stdout.reopen(StringIO.new)
$stdout.reopen('/dev/null', 'a')
$stderr.reopen($stdout)

info('daemon_pid',
Expand Down
11 changes: 8 additions & 3 deletions lib/taskjuggler/daemon/ReportServlet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,13 @@ def generateReport(projectId, reportId, attributes)
# text from the report server. This buffer will contain the generated
# report as HTML encoded text. They will be send via DRb, so we have to
# extend them with DRbUndumped.
stdOut = StringIO.new('')
#
# Note: In Ruby 4.0+, StringIO.new('') unexpectedly creates a read-only
# buffer in the web server context. Using StringIO.new without arguments
# avoids this issue. Root cause not yet identified.
stdOut = StringIO.new
stdOut.extend(DRbUndumped)
stdErr = StringIO.new('')
stdErr = StringIO.new
stdErr.extend(DRbUndumped)

begin
Expand All @@ -119,6 +123,7 @@ def generateReport(projectId, reportId, attributes)
end

error('rs_io_connect_failed', "Can't connect IO: #{$!}")
return
end

# Ask the ReportServer to generate the reports with the provided ID.
Expand Down Expand Up @@ -172,7 +177,7 @@ def generateWelcomePage(projectId)
"Cannot get project list from daemon: #{$!}")
end

text = "== Welcome to the TaskJuggler Project Server ==\n----\n"
text = +"== Welcome to the TaskJuggler Project Server ==\n----\n"
projects.each do |id|
if id == projectId
# Show the list of reports for this project.
Expand Down
86 changes: 86 additions & 0 deletions spec/ReportServlet_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env ruby -w
# frozen_string_literal: true
# encoding: UTF-8
#
# = ReportServlet_spec.rb -- The TaskJuggler III Project Management Software
#
# Copyright (c) 2026 Enno Richter <2536303+elohmeier@users.noreply.github.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#

require 'taskjuggler/Tj3Config'
require 'taskjuggler/daemon/ReportServlet'

class TaskJuggler

describe ReportServlet do

def response
Class.new do
attr_accessor :body, :status

def initialize
@headers = {}
end

def [](key)
@headers[key]
end

def []=(key, value)
@headers[key] = value
end
end.new
end

it 'generates a welcome page with projects when string literals are frozen' do
broker = double('broker', :getProjectList => [ 'example' ],
:disconnect => nil)
servlet = ReportServlet.allocate
res = response
servlet.instance_variable_set(:@res, res)
allow(AppConfig).to receive(:appName).and_return('tj3webd')
allow(servlet).to receive(:connectToBroker).and_return(broker)
allow(servlet).to receive(:getProjectName).with('example').
and_return('Example Project')

servlet.send(:generateWelcomePage, '')

res['content-type'].should eq('text/html')
res.body.should match(/Example Project/)
end

it 'uses writable buffers while generating a report' do
broker = double('broker', :getProject => [ 'project-uri', 'project-key' ],
:disconnect => nil)
projectServer = double('project server',
:getReportServer => [ 'report-uri', 'report-key' ])
reportServer = double('report server')
allow(reportServer).to receive(:connect) do |_key, stdOut, stdErr,
_stdIn, _silent|
stdOut.write('<html>Generated report</html>')
stdErr.write('')
end
allow(reportServer).to receive(:generateReport).and_return(true)
allow(reportServer).to receive(:disconnect)
allow(reportServer).to receive(:terminate)
allow(DRbObject).to receive(:new).
and_return(projectServer, reportServer)

servlet = ReportServlet.allocate
res = response
servlet.instance_variable_set(:@res, res)
allow(servlet).to receive(:connectToBroker).and_return(broker)

servlet.send(:generateReport, 'example', 'report', '')

res['content-type'].should eq('text/html')
res.body.should eq('<html>Generated report</html>')
end

end

end
2 changes: 1 addition & 1 deletion spec/support/DaemonControl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def startDaemon(config = '')
$stdout.reopen('stdout.log', 'w')
$stderr.reopen('stderr.log', 'w')
res = stdIoWrapper do
Tj3Daemon.new.main(%w( --silent ))
Tj3Daemon.new.main(%w( --silent --dont-daemonize ))
end
raise "Failed to start tj3d: #{res.stdErr}" if res.returnValue != 0
exit!
Expand Down
43 changes: 23 additions & 20 deletions taskjuggler.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
lib = File.expand_path('../lib', __FILE__)
$:.unshift lib unless $:.include?(lib)

# Get software version number from Tj3Config class.
begin
$: << 'lib'
require 'taskjuggler/Tj3Config'
PROJECT_VERSION = AppConfig.version
PROJECT_NAME = AppConfig.softwareName
rescue LoadError
raise "Error: Cannot determine software settings: #{$!}"
# Keep gem metadata loadable before runtime dependencies have been installed.
require_relative 'lib/taskjuggler/version'
PROJECT_VERSION = VERSION
PROJECT_NAME = 'TaskJuggler'

filesIn = lambda do |directory|
files = (`git ls-files -- #{directory} 2>/dev/null`).split("\n")
files.empty? ? Dir.glob("#{directory}/**/*").select { |f| File.file?(f) } : files
end

GEM_SPEC = Gem::Specification.new { |s|
Expand All @@ -45,26 +45,29 @@ management.
EOT
s.license = 'GPL-2.0-only'
s.require_path = 'lib'
s.files = (`git ls-files -- lib`).split("\n") +
(`git ls-files -- data`).split("\n") +
(`git ls-files -- manual`).split("\n") +
(`git ls-files -- examples`).split("\n") +
(`git ls-files -- tasks`).split("\n") +
s.files = filesIn.call('lib') +
filesIn.call('data') +
filesIn.call('manual') +
filesIn.call('examples') +
filesIn.call('tasks') +
%w( .gemtest taskjuggler.gemspec Rakefile ) +
# Generated files, not contained in Git repository.
%w( data/tjp.vim ) + Dir.glob('manual/html/**/*') + Dir.glob('man/*.1')
Dir.glob('manual/html/**/*') + Dir.glob('man/*.1')
s.bindir = 'bin'
s.executables = (`git ls-files -- bin`).split("\n").
s.executables = filesIn.call('bin').
map { |fn| File.basename(fn) }
s.test_files = (`git ls-files -- test`).split("\n") +
(`git ls-files -- spec`).split("\n")
s.test_files = filesIn.call('test') + filesIn.call('spec')

s.extra_rdoc_files = %w( README.rdoc COPYING CHANGELOG )
s.extra_rdoc_files = %w( README.rdoc COPYING )

s.add_dependency('base64', '>= 0.2.0')
s.add_dependency('drb', '>= 2.1.0')
s.add_dependency('mail', '~> 2.7', '>= 2.7.1')
s.add_dependency('webrick', '~> 1.9', '>= 1.9.1')
s.add_runtime_dependency('term-ansicolor', '~> 1.7', '>= 1.7.1')
s.add_development_dependency('rspec', '~> 2.5', '>= 2.5.0')
s.add_development_dependency('rake', '~> 13.0')
s.add_development_dependency('rspec', '~> 3.13')
s.add_development_dependency('test-unit', '~> 3.7')
s.platform = Gem::Platform::RUBY
s.required_ruby_version = '>= 2.0.0'
s.required_ruby_version = '>= 3.2.0'
}
69 changes: 69 additions & 0 deletions test/TestSuite/Export-Reports/refs/Leave.tjp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
project prj "Annual Leave" "1.0" 2011-12-19-00:00-+0000 - 2012-12-18-00:00-+0000 {
timezone "UTC"
scenario plan "Plan Scenario" {
active yes
}
}

projectids prj

shift s1 "Shift 1" {
workinghours sun off
workinghours mon 9:00 - 17:00
workinghours tue 9:00 - 17:00
workinghours wed 9:00 - 17:00
workinghours thu 9:00 - 17:00
workinghours fri 9:00 - 17:00
workinghours sat off
}
resource team "Team" {
resource r1 "R1"
resource r2 "R2"
}
resource r3 "R3"

task _Task_1 "foo" {
start 2011-12-19-00:00-+0000
scheduled
}
supplement task _Task_1 {
priority 500
projectid prj
}
supplement resource team {
workinghours sun off
workinghours mon 9:00 - 17:00
workinghours tue 9:00 - 17:00
workinghours wed 9:00 - 17:00
workinghours thu 9:00 - 17:00
workinghours fri 9:00 - 17:00
workinghours sat off
}
supplement resource r1 {
workinghours sun off
workinghours mon 9:00 - 17:00
workinghours tue 9:00 - 17:00
workinghours wed 9:00 - 17:00
workinghours thu 9:00 - 17:00
workinghours fri 9:00 - 17:00
workinghours sat off
}
supplement resource r2 {
workinghours sun off
workinghours mon 9:00 - 17:00
workinghours tue 9:00 - 17:00
workinghours wed 9:00 - 17:00
workinghours thu 9:00 - 17:00
workinghours fri 9:00 - 17:00
workinghours sat off
}
supplement resource r3 {
shifts s1 2011-12-19-00:00-+0000 - 2012-01-09-00:00-+0000
workinghours sun off
workinghours mon 9:00 - 17:00
workinghours tue 9:00 - 17:00
workinghours wed 9:00 - 17:00
workinghours thu 9:00 - 17:00
workinghours fri 9:00 - 17:00
workinghours sat off
}
Loading