Compare commits

..

151 Commits

Author SHA1 Message Date
Federico Hernandez
82e019a4a8 Version number and release date for 2.4.1 2015-02-16 23:38:16 +01:00
Renato Alves
9180aa6e33 Tests - minor formatting changes 2015-02-16 15:37:31 +00:00
Renato Alves
32d837fb25 Tests - Convert add.t to python
* Also added UUID_regex to basetest.utils, likely to be reused
2015-02-16 15:36:38 +00:00
Renato Alves
395b08385b Tests - Rename testname of abbreviation.t 2015-02-16 15:36:09 +00:00
Renato Alves
9cc9e19757 Tests - conversion script was failing due to binaries matching 2015-02-16 14:50:35 +00:00
Renato Alves
c07d74574d Tests - Silence "vramsteg not found" message 2015-02-16 14:36:47 +00:00
Renato Alves
73401664b6 Tests - Convert abbreviation.t to Python and add one more testcase 2015-02-16 14:30:11 +00:00
Renato Alves
d052b8752d Tests - remove unused imports 2015-02-16 14:09:04 +00:00
Renato Alves
68d9e3bb5e Tests - List perl tests to migrate with ./conversion -v 2015-02-16 03:58:49 +00:00
Renato Alves
86837022d3 Tests - conversion script was giving false positives for python 2015-02-16 03:47:12 +00:00
Renato Alves
e831f85823 Tests - Migrate bug.1006.t to python 2015-02-16 03:47:12 +00:00
Renato Alves
4b839f2c83 Tests - Significant speed improvement at cost of CPU cycles 2015-02-16 01:59:47 +00:00
Renato Alves
1e1bd32c42 Tests - Rework how taskw is launched from python
* Should avoid some odd race conditions
2015-02-16 01:59:32 +00:00
Renato Alves
880ab5d665 Tests - Correct use of non-existent exception 2015-02-15 19:43:09 +00:00
Renato Alves
ef583dedea Tests - Update template.t to include assertValidJSONOutput 2015-02-15 18:13:49 +00:00
Renato Alves
1bd26fe9ab Tests - Default to having Python objects as "hook log" 2015-02-15 18:10:36 +00:00
Renato Alves
80c01f3ffb Tests - Remove assertTriggered, redundant with assertTriggeredCount 2015-02-15 18:10:36 +00:00
Renato Alves
7cd42b5d2f Tests - Less verbose hooks.on-*.t tests 2015-02-15 18:10:36 +00:00
Renato Alves
1b218d68f5 Tests - Invalid JSON replies in hooks no longer fail the test framework 2015-02-15 18:10:36 +00:00
Paul Beckingham
e6031183dd Documentation
- Added 'taskd.trust' setting warning in the NEWS file.
2015-02-15 12:10:08 -05:00
Paul Beckingham
f9bcb8cfc8 Unit Tests
- Added unit tests for TW-1542.
2015-02-15 11:45:31 -05:00
Paul Beckingham
2ef0214248 TW-1542
- TW-1542 Large numeric UDA values get rendered in scientific notation on export
          (thanks to Ralph Bean).
2015-02-15 11:24:24 -05:00
Paul Beckingham
0a62061ca8 L10N
- Localized the new strings in Hooks.cpp.
2015-02-15 02:03:24 -05:00
Paul Beckingham
5d96547c07 Documentation
- Prepared more docs for the 2.4.1 release.
2015-02-14 21:12:16 -05:00
Paul Beckingham
eb3afa1fdc Documentation
- Updated ChangeLog.
2015-02-14 20:44:56 -05:00
Paul Beckingham
885ff46066 Unit Tests
- Renamed tw-1452.t to take it out of play, because the bug is not going to be
  fixed for 2.4.1.
2015-02-14 19:10:57 -05:00
Paul Beckingham
5a060802e7 Hooks
- It is now an error for a failing hook script to fail to also generate some
  feedback.
- Updated NEWS file to mention stricter hook script controls.
2015-02-14 18:16:32 -05:00
Paul Beckingham
acce2d5a68 Unit Tests
- Corrected expected output from on-modify-misbehave6 test hook script.
2015-02-14 17:55:02 -05:00
Paul Beckingham
adac566665 Unit Tests
- Corrected expected output from the on-modify-misbehave4 test hook script.
- Moved JSON syntax error details to debug output.
2015-02-14 17:50:28 -05:00
Paul Beckingham
f454eecafb Unit Tests
- Corrected expected output.
- Added 'complete' task to the on-modify-misbehave4 test hook script.
2015-02-14 17:46:24 -05:00
Paul Beckingham
f83702076b Unit Tests
- Updated expected error for on-modify-misbehave3 test hook script.
2015-02-14 17:44:21 -05:00
Paul Beckingham
83519a44c6 Unit Tests
- Added feedback to the on-modify-misbehave2 test hook script.
2015-02-14 17:42:03 -05:00
Paul Beckingham
a2e980c6b8 Hooks
- Deferred JSON checking until after we know whether the hooks voluntarily
  failed the event.
2015-02-14 17:33:48 -05:00
Paul Beckingham
e00704a375 Hooks
- Cleaned up logic for crude JSON detection.
2015-02-14 17:31:37 -05:00
Paul Beckingham
8fb3161a02 Hooks
- Modified expected output from an on-modify hooks script, because it's 1 line
  of JSON, not 2.
2015-02-14 17:19:44 -05:00
Tomas Babej
fa528beb58 Tests: Add test for TW-1510 2015-02-14 17:10:22 -05:00
Paul Beckingham
c5a2b7f759 Unit Tests
- Fixed exected error for on-add-misbehave5. This highlights a potential
  error in the test suite with the one failing test.
2015-02-14 17:03:43 -05:00
Paul Beckingham
4213afd408 Unit Tests
- Fixed on-add-misbehave6 error.
2015-02-14 16:52:47 -05:00
Paul Beckingham
8ad0c1172e Unit Tests
- on-add now detects JSON emitted by hooks for different tasks.
2015-02-14 16:46:52 -05:00
Paul Beckingham
f887173bf7 TW-1510
- TW-1510 Task can save empty value in the data backlog (thanks to Tomas Babej).
2015-02-14 16:33:45 -05:00
Tomas Babej
328b7ec260 Task: Do not export empty attributes in composeJSON 2015-02-14 16:32:41 -05:00
Paul Beckingham
ab41e005c9 Unit Tests
- Added on-exit test for unexpected JSON output.
2015-02-14 16:19:36 -05:00
Paul Beckingham
bd785ffad5 Hooks
- Added a new on-launch-misbehave2 script that emits unexpected JSON.
2015-02-14 16:14:20 -05:00
Paul Beckingham
3cce6c23f5 Hooks
- Hooks now verify the expected number of JSON lines emitted by hook scripts.
2015-02-14 15:50:51 -05:00
Paul Beckingham
ec919a8677 TW-1534
- TW-1534 Urgency coefficient for user project disables 'info' output(thanks to
          Martin).
- Reimplemented 'information' command urgency details as a ViewText.
- Fixed math bug in rightJustify.
2015-02-14 12:50:10 -05:00
Paul Beckingham
5911286218 Documentation
- Relocated Tomas to a better seat.
2015-02-14 11:52:07 -05:00
Paul Beckingham
9e49325d6e TW-1531
- TW-1531 'task export' should handle recurrence (thanks to Tomas Babej).
2015-02-13 20:35:04 -05:00
Tomas Babej
c483c8dda9 CmdExport: Generate recurrent tasks 2015-02-13 20:31:46 -05:00
Paul Beckingham
2d2579d801 Unit Tests
- Fixed bug in test where the rc path name contains a number, which is found by
  the test in place of an unexpected week number.
2015-02-08 14:04:22 -05:00
Paul Beckingham
4f6c51e7ae Unit Tests
- Updated README with suggested work.
- Removed obsolete TAP.py.
- Added 'conversion' script to check on the Perl -> Python progress.
2015-02-08 13:51:27 -05:00
Paul Beckingham
cd6808ab15 Unit Tests
- Removed color.duetoday.t, because of two reasons. First, the test is flawed,
  and second the test is duplicated in tw-285.t.
2015-02-08 13:22:05 -05:00
Paul Beckingham
8b648bc690 Unit Tests
- Reduced hh from 99:00:00 to 95:00:00, because with the +01:00 it becomes
  100:00:00, and '100' exceeds Nibbler::getDigit2.
2015-02-08 13:18:23 -05:00
Paul Beckingham
9a0a6188b1 Documentation
- Added email address to which patches should be sent (thanks to mathstuf).
2015-02-07 18:50:03 -05:00
Paul Beckingham
b79453afa7 TW-1532
- TW-1532 Hooks does not execute any script on Cygwin (thanks to Taisuke
          Hachimura).
2015-02-07 14:45:06 -05:00
Paul Beckingham
60dddffda3 TW-1524
- TW-1524 Build Broken (thanks to Jack).
2015-02-07 14:39:25 -05:00
Paul Beckingham
e8dc5a16a1 Portability
- Clarified the requirements regarding C++11 support, libuuid and gnutls.
2015-02-07 14:37:51 -05:00
Paul Beckingham
528e72062b Hooks
- Fixed the ::execute function used by Hooks and CmdCalendar to run commands,
  by following the convention:

    "The first argument, by convention, should point to the file name associated
     with the file being executed."
     (man execvp)
2015-02-07 14:08:58 -05:00
Paul Beckingham
876820ee1a Unit Tests
- Restored bash as the interpreter for wrapper.sh.
2015-02-07 10:39:16 -05:00
Paul Beckingham
8e730f2da3 Hooks
- Updated examples to not rely on bash being in /bin.
2015-02-07 10:25:50 -05:00
Paul Beckingham
8b28c36485 Unit Tests
- Modified the test_hooks/* scripts to use /bin/sh, which is portable. When
  using /bin/bash, Cygwin and FreeBSD silently fail.
2015-02-07 10:22:07 -05:00
Paul Beckingham
04be198281 Unit Tests
- Added on-modify hook tests.
2015-02-07 08:58:47 -05:00
Paul Beckingham
1b69ac6ad2 Unit Tests
- The on-modify-misbehave1 test is not useful.
2015-02-07 08:57:27 -05:00
Paul Beckingham
b1081bd510 Unit Tests
- Added hook tests for on-add.
2015-02-07 08:41:02 -05:00
Paul Beckingham
a72322b9db Unit Test
- Added tests for on-exit hooks.
2015-02-02 12:37:39 -05:00
Paul Beckingham
82e6574d30 Unit Tests
- Added tests for on-launch hooks.
2015-02-01 18:37:18 -05:00
Paul Beckingham
1f14454be7 Merge branch '2.4.1' of ssh://git.tasktools.org/tm/task into 2.4.1
Conflicts:
	test/template.t
2015-02-01 17:01:58 -05:00
Paul Beckingham
384ca82292 Unit Tests
- Hooks snapshot prior to pull.
2015-02-01 17:00:39 -05:00
Renato Alves
066143d939 Unittest - template.t use docstring for proper test description on execution 2015-02-01 20:39:38 +00:00
Renato Alves
e845a25ea6 Unittest - Replace the timestamp function on wrapper.sh
* %N is not supported by /bin/date on BSD systems
2015-02-01 20:35:11 +00:00
Renato Alves
75a2b11638 Unittest - Update template.t to use Hook.assert* functions 2015-02-01 19:33:08 +00:00
Renato Alves
6a3edc88a1 Unittest - properly implement Hook.assert* functions 2015-02-01 19:33:08 +00:00
Renato Alves
bb3555f217 Unittest - Bad exception formatting 2015-02-01 19:33:08 +00:00
Paul Beckingham
5de80eedeb Unit Tests
- Typos in template.t.
2015-02-01 12:36:54 -05:00
Paul Beckingham
10c1b5f902 Merge branch '2.4.1' of ssh://git.tasktools.org/tm/task into 2.4.1 2015-02-01 11:53:11 -05:00
Renato Alves
049ed04471 Unittest - Document the magic line in wrapper.sh 2015-02-01 06:23:34 +00:00
Paul Beckingham
3ff42af9c7 Unit Tests
- Added sample hooks that behave/misbehave in various ways.
2015-01-31 18:34:25 -05:00
Paul Beckingham
1cfdfbae52 Hooks
- Removed the ability for hooks to add tasks, or modify tasks that are outside
  the context of the current event. This makes hooks a local mechanism that
  operates only on local changes. Modifications/additions coming in via sync
  command are not processed by hooks.
2015-01-31 17:47:58 -05:00
Paul Beckingham
61291e4d1e C++
- ColumnDate::~ColumnDate should be virtual.
2015-01-31 13:23:45 -05:00
Tomas Babej
77ec56dcd0 Tests: Add test case for disabled recurrence mechanism 2015-01-25 16:06:26 -05:00
Paul Beckingham
d0f74c4a80 Unit Tests
- Fixed a broken test that returns different values are different times of the
  year/month.
- Removed silly tests that were looking at color values, which is a bad idea.
2015-01-25 16:00:43 -05:00
Paul Beckingham
4bdee56fa7 Configuration
- New 'recurrence' configuration setting can disable recurring task generation.
2015-01-25 15:41:31 -05:00
Paul Beckingham
8323407242 TW-1504
- TW-1504 On-modify hook does not accept correct JSON format (thanks to Tomas
          Babej).
2015-01-25 14:51:07 -05:00
Paul Beckingham
6626207ad1 TW-1522
- TW-1522 Date format doesn't like hyphens (thanks to Scott Carter).
2015-01-25 14:49:02 -05:00
Paul Beckingham
4865269630 Hooks
- Modified loop so it no longer walks the whole list unnecessarily.
- Minor style tweaks.
2015-01-25 10:33:03 -05:00
Tomas Babej
8683574b18 Hooks: Make sure that original task is properly detected
On modify event uses UUID of the original task being modified
to determine which line should be interpreted as modification
of the task.

This was achieved by searching by a substring in a specific JSON
format, which, consenquently, failed on JSON strings of other (valid)
formats.
2015-01-25 10:28:04 -05:00
Paul Beckingham
5e90510530 Merge branch '2.4.1' of ssh://git.tasktools.org/tm/task into 2.4.1 2015-01-24 10:12:43 -05:00
Tomas Babej
a8bab90c34 Documentation
- Update the man page with the correct description of behaviour of
  rc.confirmation and rc.bulk variables
2015-01-24 10:12:32 -05:00
Renato Alves
87c2a9b5a5 Unittest - README clarifications
* Add one line command to run all tests
* Clarify that only tests ending with .t and execute bit set are run
2015-01-21 12:13:19 +00:00
Paul Beckingham
cba5948a1d Unit Tests
- Took the tw-1481.t test offline, because the bug will not be fixed in the
  next release.
2015-01-20 22:06:46 -05:00
Paul Beckingham
f4a6ec6f97 Documentation
- Updated README.
2015-01-20 22:05:11 -05:00
Paul Beckingham
a3490966c0 Documentation
- Added README to explain the test suite.
2015-01-20 21:52:17 -05:00
Paul Beckingham
855537b975 TW-1517
- TW-1517 Hook performance should be measured individually for each hook (thanks
          to Tomas Babej).
2015-01-20 20:33:31 -05:00
Paul Beckingham
473019c1f0 TW-1519
- TW-1519 Testing suite forces taskd.trust="ignore hostname" (thanks to Renato
          Alves).
2015-01-20 20:18:13 -05:00
Renato Alves
d1698eab2d Fix usage of taskd.trust=strict on the test suite
* This reverts commit 67cb30fdce.
* Code also now tests if taskd is listening on IPv4 or IPv6 interfaces.
2015-01-20 11:56:53 +00:00
Paul Beckingham
73b7b21b81 Certs
- Replaced all the certs for testing.  This may not help, but it does mean
  there were recreated using the current PKI scripts, and are all consistent.
2015-01-19 18:25:21 -05:00
Paul Beckingham
44f2c0c98c Unit Tests
- Added JSON test which includes spaces.
2015-01-19 18:09:54 -05:00
Tomas Babej
654159b2fd Hooks: Add per-hook time measurement 2015-01-19 16:07:34 -05:00
Renato Alves
67cb30fdce Unittest - default to taskd.trust="ignore hostname"
* The currently available taskd testing certificates fail if
taskd.trust is set to strict. See also: TW-1519
2015-01-19 15:07:06 +00:00
Renato Alves
7d75547f44 Ignore taskd in src folder (for running taskd tests) 2015-01-19 15:00:02 +00:00
Renato Alves
d68fa7ea8a Unittest - Template updated to include hook test examples 2015-01-19 14:58:06 +00:00
Renato Alves
db78851b40 Unittest - Hook testing, fixed a few mistakes 2015-01-19 14:54:11 +00:00
Tomas Babej
6fdf0738a7 Hooks: Improve debugging messages for on-exit hooks 2015-01-18 13:27:32 -05:00
Paul Beckingham
41003fc78b Code Cleanup
- Removed whitespace at EOL.
2015-01-17 19:05:09 -05:00
Wilhelm Schuermann
d2928dc4e4 TW-1516 Hook input not ended with EOF
- Made execute() send EOF when all input is written.
- Minor code cleanup.
2015-01-17 19:04:31 -05:00
Wilhelm Schuermann
61deb7ce7e CMake
- Fix CMAKE_CXX_FLAGS change to preserve previous flags.
2015-01-17 18:58:38 -05:00
Renato Alves
0311ea6689 GnuTLS optionally compiled - defaults to yes
* Allow optionally disabling GnuTLS via config var (USE_GNUTLS)
2015-01-14 13:17:25 +00:00
Paul Beckingham
0b0ddbfbc4 TW-1491
- TW-1491 Regression in deleting due dates (thanks to Jens Erat).
2015-01-12 15:52:15 -05:00
Jens Erat
cd31335742 TW-1491 - TW-1491 Regression in deleting due dates 2015-01-12 15:42:58 -05:00
Tomas Babej
5a3037f257 Tests: Fix error in export.t and add test for urgency 2015-01-12 15:38:44 -05:00
Paul Beckingham
a22d173b9b TW-1487
- TW-1487 Task export exports some numeric attributes as strings (thanks to
          Tomas Babej).
2015-01-11 09:07:04 -05:00
Paul Beckingham
134d201cb0 TW-1457
- TW-1457 Non-existant attributes are not properly handled (thanks to Tomas
          Babej).
2015-01-10 15:53:55 -05:00
Paul Beckingham
547d9edb55 TW-1484
- TW-1484 The 'history' and 'ghistory' reports do not obey rc.color.label.
2015-01-10 15:49:42 -05:00
Paul Beckingham
cdde747733 TW-1498
- TW-1498 Filtering for presence of UDA matches all tasks (thanks to Ralph Bean).
2015-01-10 14:36:21 -05:00
Paul Beckingham
588009d679 TW-1501
- TW-1501 Calc can't handle multi-digit numbers in some expressions (thanks to
          Jeremy John Reeder).
2015-01-08 20:40:29 -05:00
Paul Beckingham
adf9f25289 Unit Tests
- Removed failing tests, as there is no plan to address all these now.
2015-01-07 23:43:11 -05:00
Paul Beckingham
ea0397f542 TW-1495
- TW-1495 German translation for taskwarrior (thanks to Jens Erat).
2015-01-07 23:23:25 -05:00
Paul Beckingham
411fab3a27 Hooks
- Added debug output on startup, if a hook script is found and does not have
  a valid name.
2015-01-07 22:54:55 -05:00
Paul Beckingham
da6a57b203 Hooks
- The 'diag' command should indicate misnamed hook scripts (thanks to Tomas
  Babej).
2015-01-07 18:43:14 -05:00
Tomas Babej
235e4ef047 Tests: Add negative tests for parsing time 2015-01-07 17:14:49 -05:00
Tomas Babej
1dbb0a57a8 Tests: Add negative tests for parsing dates 2015-01-06 19:14:04 -05:00
Paul Beckingham
5a7117630e Unit Tests
- Typo.
2015-01-05 20:04:26 -05:00
Paul Beckingham
e9f9f6a86e Unit Tests
- Added copyright and removed whitespace at EOL.
2015-01-05 19:52:17 -05:00
Tomas Babej
8ea1563944 Tests: Add basic coverage for the types of attributes in the export command 2015-01-05 19:48:52 -05:00
Wilhelm Schuermann
e522c1980a TW-1424
- Fixed tw-1424.t (mostly).
2015-01-05 06:53:51 -05:00
Paul Beckingham
845fe047cc C++11
- Converted one source file to use an actual C++11 feature. If no one notices
  we can move forward and start using supported featues. If it's a problem,
  this commit gets rolled back.

  GCC 4.6 (released March 2011), and Clang 3.0 (releasd November 2011) both
  support N2930 (range-based for) which is the feature being tried here.
2015-01-04 22:01:22 -05:00
Paul Beckingham
5932d9f90b Portability
- More STDOUT_FILENO conversions.
2015-01-04 22:00:08 -05:00
Paul Beckingham
a8ff7655ef Portability
- Properly detects Cygwin.  version 2.
2015-01-04 21:37:47 -05:00
Paul Beckingham
79cc9d8ed9 Portability
- Properly detects Cygwin.
2015-01-04 21:29:46 -05:00
Paul Beckingham
b83dc8a29e Portability
- Added '-std=gnu++0x' for Cygwin.
2015-01-04 20:39:42 -05:00
Paul Beckingham
99ebf82c3a Context
- Eliminated fileno() by using unistd.h STDOUT_FILENO. This is because fileno()
  is not visible with '--std=c++11'.
2015-01-04 20:25:25 -05:00
Paul Beckingham
231e8ca913 Portability
- Missing include on Cygwin only.  Odd.
2015-01-04 20:17:56 -05:00
Paul Beckingham
9bd9a3b285 C++
- Enabling C++11, as a test.  Let's see if anything blows up.
2015-01-04 20:11:32 -05:00
Paul Beckingham
b882cd151e Build
- Added code to set poicy CMP0037 to "OLD", which eliminates the warnings about
  the 'test' target.
2015-01-04 18:59:09 -05:00
Paul Beckingham
a3b0d5c6a6 Unit Tests
- Added copyright to tests that were missing it.
2015-01-04 10:55:01 -05:00
Paul Beckingham
d1664b3f55 Unit Tests
- Re-enabled bug.982.t and fixed the regex so it is right. This test was created
  for a parsing bug which is now fixed.
2015-01-04 10:40:21 -05:00
Paul Beckingham
1cac14f3f8 Unit Tests
- This disabled test is verifying 'Total Active Time' functionality that has
  itself been removed.
2015-01-04 10:35:57 -05:00
Paul Beckingham
507e2d5807 Unit Tests
- Added copyright, fixed test name.
2015-01-04 10:30:10 -05:00
Tomas Babej
c664d62c2f Tests: Add coverage for TW-1452 2015-01-04 10:23:38 -05:00
Paul Beckingham
f32c4d1f73 TW-1492
- TW-1492 compiling v2.4.0 using musl(libc) (thanks to V.Krishn).
2015-01-04 10:14:15 -05:00
Paul Beckingham
9bf7dfda4f AUTHORS
- Upgrade Tomas to the dev section.
2015-01-03 20:25:30 -05:00
Tomas Babej
2a782b8f01 Tests: Add coverage for TW-1481 2015-01-03 20:21:46 -05:00
Paul Beckingham
3c07510a43 Unit Tests
- Added copyright to template.
2015-01-03 20:20:30 -05:00
Paul Beckingham
07eff0f3c1 Bug
- Changed assorted reports so they do not use '.age' format for dates that are
  in the future, because those are never shown with this format.
2015-01-03 17:56:20 -05:00
Paul Beckingham
dad0ac0c27 TW-1486
- TW-1486 task wait shows completed tasks which has a wait attribute (thanks to
          Sujeevan Vijayakumaran).
2015-01-03 09:31:54 -05:00
Paul Beckingham
2c986d6e6b Unit Tests
- Fixed typo in comment.
2015-01-03 09:24:07 -05:00
Paul Beckingham
1e8f83cab9 Themes
- Removed 'bold' from a 256-color definition.
2015-01-02 08:55:18 -05:00
Paul Beckingham
15f00452c2 Themes
- Increased contrast for 'color.blocking'.
2015-01-02 08:54:50 -05:00
Paul Beckingham
6a3b13dbfc Documentation
- Initial project version doc updates.
2015-01-02 00:01:32 -05:00
Federico Hernandez
8180b94b1b Bumped version number in task reference pdf 2015-01-02 01:05:03 +01:00
Federico Hernandez
6aafa9f04e Bumped version number to 2.4.1 2015-01-02 00:44:49 +01:00
Federico Hernandez
fe07f62214 Added SHA1 of tagged release commit 2015-01-02 00:39:40 +01:00
156 changed files with 6750 additions and 1672 deletions

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@ cmake.h
commit.h
Makefile
src/task
src/taskd
src/libtask.a
src/commands/libcommands.a
src/columns/libcolumns.a

View File

@@ -13,6 +13,7 @@ contributions of the following people:
Louis-Claude Canon (Contributing Author)
Scott Kostyshak (Contributing Author)
Renato Alves (Contributing Author)
Tomas Babej (Contributing Author)
The following submitted code, packages or analysis, and deserve special thanks:
@@ -112,6 +113,8 @@ The following submitted code, packages or analysis, and deserve special thanks:
Jeremy John Reeder
Roman Inflianskas
Łukasz Panek
V.Krishn
Jens Erat
Thanks to the following, who submitted detailed bug reports and excellent
suggestions:
@@ -233,10 +236,12 @@ suggestions:
dev-zero
Petteri
Black Ops Testing
Jens Erat
Leon Feng
Tomas Babej
Nick Person
Arnoud K
Ozgur Akgun
David Costa
Sujeevan Vijayakumaran
Scott Carter
Taisuke Hachimura
Martin

View File

@@ -1,5 +1,8 @@
cmake_minimum_required (VERSION 2.8)
set(CMAKE_LEGACY_CYGWIN_WIN32 0) # Remove when CMake >= 2.8.4 is required
if(POLICY CMP0037)
cmake_policy(SET CMP0037 OLD)
endif()
include (CheckFunctionExists)
include (CheckStructHasMember)
@@ -7,12 +10,17 @@ include (CheckStructHasMember)
set (HAVE_CMAKE true)
project (task)
set (PROJECT_VERSION "2.4.0")
set (PROJECT_VERSION "2.4.1")
OPTION(USE_GNUTLS "Build gnutls support." ON)
message ("CMAKE_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set (LINUX true)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set (DARWIN true)
set (CMAKE_CXX_FLAGS "-std=c++11 -stdlib=libc++")
elseif (${CMAKE_SYSTEM_NAME} MATCHES "kFreeBSD")
set (KFREEBSD true)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
@@ -25,6 +33,9 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
set (SOLARIS true)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "GNU")
set (GNUHURD true)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "CYGWIN")
set (CYGWIN true)
set (CMAKE_CXX_FLAGS "-std=gnu++0x")
else (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set (UNKNOWN true)
endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
@@ -65,13 +76,15 @@ set (PACKAGE_TARNAME "${PACKAGE}")
set (PACKAGE_VERSION "${VERSION}")
set (PACKAGE_STRING "${PACKAGE} ${VERSION}")
message ("-- Looking for GnuTLS")
find_package (GnuTLS)
if (GNUTLS_FOUND)
set (HAVE_LIBGNUTLS true)
set (TASK_INCLUDE_DIRS ${TASK_INCLUDE_DIRS} ${GNUTLS_INCLUDE_DIR})
set (TASK_LIBRARIES ${TASK_LIBRARIES} ${GNUTLS_LIBRARIES})
endif (GNUTLS_FOUND)
if (USE_GNUTLS)
message ("-- Looking for GnuTLS")
find_package (GnuTLS)
if (GNUTLS_FOUND)
set (HAVE_LIBGNUTLS true)
set (TASK_INCLUDE_DIRS ${TASK_INCLUDE_DIRS} ${GNUTLS_INCLUDE_DIR})
set (TASK_LIBRARIES ${TASK_LIBRARIES} ${GNUTLS_LIBRARIES})
endif (GNUTLS_FOUND)
endif (USE_GNUTLS)
#message ("-- Looking for pthread")
#find_path (PTHREAD_INCLUDE_DIR pthread.h)

View File

@@ -1,10 +1,60 @@
2.4.0 (2015-01-01) -
2.4.1 (2015-02-16) -
- TW-1457 Non-existent attributes are not properly handled (thanks to Tomas
Babej).
- TW-1484 The 'history' and 'ghistory' reports do not obey rc.color.label.
- TW-1486 task wait shows completed tasks which has a wait attribute (thanks to
Sujeevan Vijayakumaran).
- TW-1487 Task export exports some numeric attributes as strings (thanks to
Tomas Babej).
- TW-1491 Regression in deleting due dates (thanks to Jens Erat).
- TW-1492 compiling v2.4.0 using musl(libc) (thanks to V.Krishn).
- TW-1495 German translation for taskwarrior (thanks to Jens Erat).
- TW-1498 Filtering for presence of UDA matches all tasks (thanks to Ralph
Bean).
- TW-1501 Calc can't handle multi-digit numbers in some expressions (thanks to
Jeremy John Reeder).
- TW-1502 Successful on-add hook with no output confuses TW (thanks to Tomas
Babej).
- TW-1504 On-modify hook does not accept correct JSON format (thanks to Tomas
Babej).
- TW-1505 completely ignore mis-named hook scripts (thanks to Tomas Babej,
David Patrick).
- TW-1509 Hooks modifications performed on sync not sycning back (thanks to
Tomas Babej).
- TW-1510 Task can save empty value in the data backlog (thanks to Tomas Babej).
- TW-1517 Hook performance should be measured individually for each hook (thanks
to Tomas Babej).
- TW-1518 Misbehaving hooks silently break task processing (thanks to Wim
Schuermann).
- TW-1519 Testing suite forces taskd.trust="ignore hostname" (thanks to Renato
Alves).
- TW-1522 Date format doesn't like hyphens (thanks to Scott Carter).
- TW-1524 Build Broken (thanks to Jack).
- TW-1530 Multiple on-add hooks generating new tasks are ignored (thanks to
Tomas Babej).
- TW-1531 'task export' should handle recurrence (thanks to Tomas Babej).
- TW-1532 Hooks does not execute any script on Cygwin (thanks to Taisuke
Hachimura).
- TW-1534 Urgency coefficient for user project disables 'info' output (thanks to
Martin).
- TW-1542 Large numeric UDA values get rendered in scientific notation on export
(thanks to Ralph Bean).
- Fixed assorted color theme problems.
- Changed assorted reports so they do not use '.age' format for dates that are
in the future, because those are never shown with this format (thanks to
Sujeevan Vijayakumaran).
- New 'recurrence' configuration setting can disable recurring task generation.
------ current release ---------------------------
2.4.0 (2015-01-01) 670102842c39bdc62ef84ae4b679a8f5a2d89523
- TD-42 Cannot compile taskd - GNUTLS_VERSION undefined in diag.cpp (thanks
to Michele Vetturi).
- TD-45 Fix preprocessor define (thanks to Jochen Sprickerhof).
- TD-55 TLSServer/Client need to include <errno.h> on Solaris (thanks to Tatjana
Heuser). Also applied to NetBSD.
- TD-55 TLSServer/Client need to include <errno.h> on Solaris (thanks to
Tatjana Heuser). Also applied to NetBSD.
- TD-56 File.cpp needs to include <string.h> on Solaris (thanks to Tatjana
Heuѕer).
- TD-57 taskdctl script assumes /bin/sh is /bin/bash (thanks to Tatjana Heuser).
@@ -286,8 +336,6 @@
- Supports 'debug.parser' configuration setting.
- Supports 'color.label.sort' for column labels of sort columns.
------ current release ---------------------------
2.3.0 (2014-01-15) c4eb46507031b7dee839dcb932bb2a22b2f0d3a2
Features

View File

@@ -1,9 +1,16 @@
How to Build Taskwarrior
Satisfy the Requirements:
- gcc 4.4 or later, clang 2.9 or later or equivalent. This is because 2.4.1
marks the first step towards using modern C++11. Initially this involves
only feature N1984.
- libuuid
- gnutls (optional)
Obtain and build code:
$ git clone https://git.tasktools.org/scm/tm/task.git task.git
$ cd task.git
$ git checkout 2.4.0 # Latest dev branch
$ git checkout 2.4.1 # Latest dev branch
$ cmake -DCMAKE_BUILD_TYPE=debug . # debug or release. Default: neither.
$ make VERBOSE=1 # Shows details
@@ -18,8 +25,9 @@ How to Build Taskwarrior
current development branch. The source tarballs do not reflect HEAD, and do
not contain the test suite.
If you send us a patch, make sure that patch is made against git HEAD. We
cannot apply patches made against the tarball source.
If you send a patch (support@taskwarrior.org), make sure that patch is made
against git HEAD on the development branch. We cannot apply patches made
against the tarball source, or master.
General Statement
This file is intended to convey the current efforts, priorities and needs of
@@ -181,12 +189,11 @@ Work in Progress
Current Codebase Condition
'master' branch:
- 2.3.0 Current release, locked.
- 2.4.1 Current release, locked.
'2.4.0' branch:
- Current development branch with new command line parser, expressions,
and heading for beta soon.
'2.4.2' branch:
- Current development branch no plans yet.
---
2014-10-12 Updated for 2.4.0
2015-02-14 Updated for 2.4.1

View File

@@ -24,7 +24,7 @@ Command Line Parsing
determines whether subsequent arguments are interpreted as part of a filter or
set of modifications.
The Parser object is fed command line arguments, then through a succession of
The CLI object is fed command line arguments, then through a succession of
calls builds and annotates a parse tree. To help with this, the Lexer is
used to break up strings into tokens.

15
INSTALL
View File

@@ -10,6 +10,13 @@ Pre-requisites
You will need the CMake build system installed in order to build taskwarrior
from source.
You will need a C++ compiler that support C++11 N1984, which includes:
- gcc 4.4 (released 2009-04-21)
- clang 2.9 (released 2011-04-06)
- uuid lib
- gnutls (optional)
More information on cmake can be obtained at http://cmake.org
It is HIGHLY RECOMMENDED that you build with a library that provides uuid_*
@@ -92,10 +99,12 @@ Currently the defined languages are:
eng_USA 1
esp_ESP 2
deu_DEU 3
fra_FRA 4
ita_ITA 5
por_PRT 6
epo_RUS 7
pol_POL 8
Uninstallation
--------------
@@ -131,9 +140,7 @@ Cygwin
Darwin, 32bit
The taskwarrior packages will not work on a 32-bit OSX installation on Core
Duo hardware. You will need to build Taskwarrior from source, and use this
configure command:
Taskwarrior needs to be built from source, and use this configure command:
cmake -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32 -DCMAKE_EXE_LINKER_FLAGS=-m32 .

100
NEWS
View File

@@ -1,92 +1,46 @@
New Features in taskwarrior 2.4.0
New Features in taskwarrior 2.4.1
- The 'show' command displays default configuration values, when appropriate.
- Removed deprecated commands 'push', 'pull' and 'merge'.
- Portuguese (por-PRT), French (fra-FRA), Esperanto (epo-RUS) and Polish
(pol-POL) localizations.
- Better handling for deletion of recurring tasks.
- New virtual tags: YESTERDAY, TOMORROW, READY, PENDING, COMPLETED, DELETED,
TAGGED.
- The '_get' command properly uses exit codes.
- Regular expressions are now enabled by default.
- The 'filter' verbosity token shows the complete filter used for the last
command.
- The 'new-uuid' verbosity token shows the UUID of newly created tasks.
- The 'info' report now breaks down urgency values.
- New 'color.until' color rule.
- Using a non-zero 'urgency.inherit.coefficient' value means a task inherits
the urgency values of the dependency chain.
- Listing breaks now supported. See 'man taskrc'.
- New fish shell completion script.
- The filter form 'name:value' now maps to the partial match operator '=',
rather than the exact match operator, '=='. This means that dates now
match on the day by default, not the time also.
- UDA indicator format.
- Hooks.
- New and updated holidays.rc files.
- New German translation.
- Hook scripts are now under much stricter control.
New commands in taskwarrior 2.4.0
New commands in taskwarrior 2.4.1
- New 'calc' command (and standalone utility) for quick command line
calculations.
- None
New configuration options in taskwarrior 2.4.0
New configuration options in taskwarrior 2.4.1
- The 'taskd.trust' setting is now a tri-state, supporting values 'strict',
'ignore hostname' and 'allow all', for server certificate validation.
- New theme: dark-gray-blue-256.theme
- The 'allow.empty.filter' setting can be set to 'no' to disallow the
potentially dangerous combination of write commands and empty filters.
- New truncated_count column style for the description field which as the
name says is a combination of the existing truncated and count styles.
- New 'hooks' setting is a master control switch for hook processing.
- New 'debug.hooks' for debugging hook scripts.
- New 'debug.parser' for debugging parser issues scripts.
- New 'color.label.sort' is used to color the column label of sort columns.
- New 'urgency.uda.<name>.<value>' allows specific UDA values to affect
urgency.
- New 'recurrence.confirmation' which allows bypassing confirmation for
changes to recurring tasks, by accepting, rejecting or prompting.
- New 'uda.<name>.indicator' to override the UDA indicator.
- The 'date.iso' setting allows you to enable (default) or disable support
for ISO-8601 dates. This is because some of you have 'dateformat' settings
that conflict.
- The 'recurrence' setting enables (default) or disables recurring task
instance generation.
Newly deprecated features in taskwarrior 2.4.0
Newly deprecated features in taskwarrior 2.4.1
- The alias '_query' is deprecated.
- Bare word search terms are deprecated, so 'task pattern list' will not
be supported in future releases, instead requiring 'task /pattern/ list'.
- The 'limit' pseudo attribute will change to 'rc.limit' in a future release.
- None
Removed features in 2.4.0
Removed features in 2.4.1
- Version 1.x column names no longer supported.
- Version 1.x sort columns no longer supported.
- Old-style color names including underscores are no longer supported.
- Removed priority counts from the 'projects' commands.
- Removed the unused 'locale' configuration variable.
- Removed the unused 'patterns' confguration variable.
- Removed the obsolete 'task-faq.5' man page.
- Removed the obsolete 'task-tutorial.5' man page.
- Removed the obsolete 'tasksh.1' man page.
- Removed the 'tasksh' program from the Taskwarrior distribution. It is now
a separate project.
- Removed the 'complete.all.projects' setting that was not used.
- Removed support for 'report.X.limit', which can now be set in the report
filter 'limit:N'.
- The 'total active time' information is removed from the 'info' report.
This was being misinterpreted as support for time tracking.
- The 'shadow file' feature is removed, but replaced with an example hook
script that performs the same function..
- None
Known Issues
- On Cygwin, case-insensitive regex searches are broken. The default is
now case-sensitive for Cygwin only.
- https://bug.tasktools.org
- If you upgraded from Taskwarrior 2.3.0, you will need one of the following
settings to allow continued syncing to a Taskserver:
$ task config taskd.trust strict
$ task config taskd.trust 'ignore hostname'
$ task config taskd.trust 'allow all'
These are presented in order of preference from most to least secure, and
depend on how your certs were generated.
- https://bug.tasktools.org/
Taskwarrior has been built and tested on the following configurations:
* OS X 10.10 Yosemite, 10.9 Mavericks
* OS X 10.10 Yosemite
* Fedora 20 Heisenbug
* Ubuntu 14.04 Trusty Tahr
* Debian 7.0 Wheezy (stable)

View File

@@ -1,4 +1,4 @@
.TH task-color 5 2015-01-01 "${PACKAGE_STRING}" "User Manuals"
.TH task-color 5 2015-02-16 "${PACKAGE_STRING}" "User Manuals"
.SH NAME
task-color \- A color tutorial for the taskwarrior command line todo manager.

View File

@@ -1,4 +1,4 @@
.TH task-sync 5 2015-01-01 "${PACKAGE_STRING}" "User Manuals"
.TH task-sync 5 2015-02-16 "${PACKAGE_STRING}" "User Manuals"
.SH NAME
task-sync \- A discussion and tutorial for the various task(1) data

View File

@@ -1,4 +1,4 @@
.TH task 1 2015-01-01 "${PACKAGE_STRING}" "User Manuals"
.TH task 1 2015-02-16 "${PACKAGE_STRING}" "User Manuals"
.SH NAME
task \- A command line todo manager.

View File

@@ -1,4 +1,4 @@
.TH taskrc 5 2015-01-01 "${PACKAGE_STRING}" "User Manuals"
.TH taskrc 5 2015-02-16 "${PACKAGE_STRING}" "User Manuals"
.SH NAME
taskrc \- Configuration details for the task(1) command
@@ -282,9 +282,8 @@ and project. The others are sent to standard output.
.TP
.B confirmation=yes
May be "yes" or "no", and determines whether taskwarrior will ask for
confirmation before deleting a task, performing bulk changes, or the undo
command. The default value is "yes". Consider leaving this setting as "yes",
for safety.
confirmation before deleting a task or performing the undo command. The default
value is "yes". Consider leaving this setting as "yes", for safety.
.TP
.B allow.empty.filter=yes
@@ -313,9 +312,9 @@ Controls padding between columns of the report output. Default is "1".
.TP
.B bulk=3
Is a number, defaulting to 3. When this number or greater of tasks are modified
in a single command, confirmation will be required, unless the
in a single command, confirmation will be required, regardless of the value of
.B confirmation
variable is "no".
variable.
This is useful for preventing large-scale unintended changes.
@@ -412,6 +411,11 @@ The character or string to show in the depends.indicator column. Defaults to +.
.B uda.<name>.indicator=U
The character or string to show in the <uda>.indicator column. Defaults to U.
.TP
.B recurrence=yes
Controls whether recurrence is enable, and whether recurring tasks continue to
generate new task instances. Defaults to 'yes'.
.TP
.B recurrence.confirmation=prompt
Controls whether changes to recurring tasks propagates to other child tasks with
@@ -616,6 +620,10 @@ field that is set. Otherwise, they are set to the corresponding values of
.RE
.RE
.TP
.B date.iso=yes
Enables ISO-8601 date support. The default value is "yes".
.TP
.B weekstart=Sunday
Determines the day a week starts. Valid values are Sunday or Monday only. The

View File

@@ -44,7 +44,7 @@ color.recurring=rgb013
color.scheduled=on rgb001
color.until=
color.blocked=white on color8
color.blocking=white on color15
color.blocking=black on color15
# Project
color.project.none=

View File

@@ -44,7 +44,7 @@ color.recurring=
color.scheduled=
color.until=
color.blocked=bold gray10 on gray4
color.blocking=bold gray18 on gray6
color.blocking=gray18 on gray6
# Project
color.project.none=

Binary file not shown.

Binary file not shown.

View File

@@ -1,20 +1,19 @@
#!/bin/bash
#!/bin/sh
# The on-add event is triggered separately for each task added
# The on-add event is triggered separately for each task added. This hook
# script can accept/reject the addition. Processing will continue.
# Input:
# - line of JSON for the task added
# Output:
# - all emitted JSON lines are added/modified as tasks, if the exit code is
# zero, otherwise ignored.
# - minimal new task: {"description":"Buy milk"}
# - to modify a task include complete JSON
# - all emitted non-JSON lines are considered feedback messages if the exit
# code is zero, otherwise they are considered errors.
# - Line of JSON for proposed new task.
read new_task
# Output:
# - JSON, modified or unmodified.
# - Optional feedback/error.
echo $new_task
echo 'on-add'
# Status:
# - 0: JSON accepted, non-JSON is feedback.
# - non-0: JSON ignored, non-JSON is error.
exit 0

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
read new_task

View File

@@ -1,20 +1,20 @@
#!/bin/bash
#!/bin/sh
# The on-exit event is triggered once, after all processing is complete, i.e.
# last
# The on-exit event is triggered once, after all processing is complete.
# This hooks script has no effect on processing.
# Input:
# - read-only line of JSON for each task added/modified
# Output:
# - any emitted JSON is ignored
# - all emitted non-JSON lines are considered feedback messages if the exit
# code is zero, otherwise they are considered errors.
while read -t 1 modified_task
# - Read-only line of JSON for each task added/modified
while read modified_task
do
# Scan task
echo $modified_task
done
# Output:
# - Optional feedback/error.
echo 'on-exit'
# Status:
# - 0: JSON ignored, non-JSON is feedback.
# - non-0: JSON ignored, non-JSON is error.
exit 0

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# This hook script replaces the shadow file feature, found in Taskwarrior
# prior to version 2.4.0.

View File

@@ -1,18 +1,17 @@
#!/bin/bash
#!/bin/sh
# The on-launch event is triggered once, after initialization, before any
# processing occurs, i.e first
# processing occurs. This hooks script has no effect on processing.
# Input:
# - none
# - None
# Output:
# - all emitted JSON lines are added/modified as tasks, if the exit code is
# zero, otherwise ignored.
# - minimal new task: {"description":"Buy milk"}
# - to modify a task include complete JSON
# - all emitted non-JSON lines are considered feedback messages if the exit
# code is zero, otherwise they are considered errors.
# - Optional feedback/error.
echo 'on-launch'
# Status:
# - 0: JSON ignored, non-JSON is feedback.
# - non-0: JSON ignored, non-JSON is error.
exit 0

View File

@@ -1,22 +1,21 @@
#!/bin/bash
#!/bin/sh
# The on-modify event is triggered separately for each task added or modified
# The on-modify event is triggered separately for each task modified. This hook
# script can accept/reject the modification. Processing will continue.
# Input:
# - line of JSON for the original task
# - line of JSON for the modified task, the diff being the modification
# Output:
# - all emitted JSON lines are added/modified as tasks, if the exit code is
# zero, otherwise ignored.
# - minimal new task: {"description":"Buy milk"}
# - to modify a task include complete JSON
# - all emitted non-JSON lines are considered feedback messages if the exit
# code is zero, otherwise they are considered errors.
read original_task
read modified_task
# Output:
# - JSON, modified or unmodified.
# - Optional feedback/error.
echo $modified_task
echo 'on-modify'
# Status:
# - 0: JSON accepted, non-JSON is feedback.
# - non-0: JSON ignored, non-JSON is error.
exit 0

View File

@@ -82,6 +82,7 @@ std::string Config::_defaults =
"# # Comma-separated list. May contain any subset of:\n"
"#verbose=blank,header,footnote,label,new-id,new-uuid,affected,edit,special,project,sync,filter\n"
"confirmation=yes # Confirmation on delete, big changes\n"
"recurrence=yes # Enable recurrence\n"
"recurrence.confirmation=prompt # Confirmation for propagating changes among recurring tasks (yes/no/prompt)\n"
"allow.empty.filter=yes # An empty filter gets a warning and requires confirmation\n"
"indent.annotation=2 # Indent spaces for annotations\n"
@@ -116,6 +117,7 @@ std::string Config::_defaults =
"dateformat.info=Y-M-D H:N:S # Preferred display date format for information\n"
"dateformat.report= # Preferred display date format for reports\n"
"dateformat.annotation= # Preferred display date format for annotations\n"
"date.iso=yes # Enable ISO date support\n"
"weekstart="
STRING_DATE_SUNDAY_LONG
" # Sunday or Monday only\n"
@@ -358,19 +360,19 @@ std::string Config::_defaults =
"\n"
"report.long.description=All details of tasks\n"
"report.long.labels=ID,A,Created,Mod,Deps,P,Project,Tags,Recur,Wait,Sched,Due,Until,Description\n"
"report.long.columns=id,start.active,entry,modified.age,depends,priority,project,tags,recur,wait.age,scheduled,due,until,description\n"
"report.long.columns=id,start.active,entry,modified.age,depends,priority,project,tags,recur,wait.remaining,scheduled,due,until,description\n"
"report.long.filter=status:pending\n"
"report.long.sort=modified-\n"
"\n"
"report.list.description=Most details of tasks\n"
"report.list.labels=ID,Active,Age,D,P,Project,Tags,R,Sch,Due,Until,Description,Urg\n"
"report.list.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur.indicator,scheduled.countdown,due,until.age,description.count,urgency\n"
"report.list.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur.indicator,scheduled.countdown,due,until.remaining,description.count,urgency\n"
"report.list.filter=status:pending\n"
"report.list.sort=start-,due+,project+/,urgency-\n"
"\n"
"report.ls.description=Few details of tasks\n"
"report.ls.labels=ID,A,D,Project,Tags,R,Wait,S,Due,Until,Description\n"
"report.ls.columns=id,start.active,depends.indicator,project,tags,recur.indicator,wait.age,scheduled.countdown,due.countdown,until.countdown,description.count\n"
"report.ls.columns=id,start.active,depends.indicator,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due.countdown,until.countdown,description.count\n"
"report.ls.filter=status:pending\n"
"report.ls.sort=start-,description+\n"
"\n"
@@ -382,13 +384,13 @@ std::string Config::_defaults =
"\n"
"report.newest.description=Newest tasks\n"
"report.newest.labels=ID,Active,Created,Age,Mod,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n"
"report.newest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.age,scheduled.countdown,due,until.age,description\n"
"report.newest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due,until.age,description\n"
"report.newest.filter=(status:pending or status:waiting) and recur.none:\n"
"report.newest.sort=entry-\n"
"\n"
"report.oldest.description=Oldest tasks\n"
"report.oldest.labels=ID,Active,Created,Age,Mod,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n"
"report.oldest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.age,scheduled.countdown,due,until.age,description\n"
"report.oldest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due,until.age,description\n"
"report.oldest.filter=(status:pending or status:waiting)\n"
"report.oldest.sort=entry+\n"
"\n"
@@ -400,7 +402,7 @@ std::string Config::_defaults =
"\n"
"report.active.description=Active tasks\n"
"report.active.labels=ID,Started,Active,Age,D,P,Project,Tags,Recur,W,Sch,Due,Until,Description\n"
"report.active.columns=id,start,start.age,entry.age,depends.indicator,priority,project,tags,recur,wait.indicator,scheduled.age,due,until,description\n"
"report.active.columns=id,start,start.age,entry.age,depends.indicator,priority,project,tags,recur,wait.indicator,scheduled.remaining,due,until,description\n"
"report.active.filter=status:pending and +ACTIVE\n"
"report.active.sort=project+,start+\n"
"\n"
@@ -412,30 +414,30 @@ std::string Config::_defaults =
"\n"
"report.recurring.description=Recurring Tasks\n"
"report.recurring.labels=ID,Active,Age,D,P,Project,Tags,Recur,Sch,Due,Until,Description,Urg\n"
"report.recurring.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur,scheduled.countdown,due,until.age,description,urgency\n"
"report.recurring.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur,scheduled.countdown,due,until.remaining,description,urgency\n"
"report.recurring.filter=(status:pending or status:waiting) and (+PARENT or +CHILD)\n"
"report.recurring.sort=due+,urgency-,entry+\n"
"\n"
"report.waiting.description=Waiting (hidden) tasks\n"
"report.waiting.labels=ID,A,Age,D,P,Project,Tags,R,Wait,for,Sched,Due,Until,Description\n"
"report.waiting.columns=id,start.active,entry.age,depends.indicator,priority,project,tags,recur.indicator,wait,wait.age,scheduled,due,until,description\n"
"report.waiting.columns=id,start.active,entry.age,depends.indicator,priority,project,tags,recur.indicator,wait,wait.remaining,scheduled,due,until,description\n"
"report.waiting.filter=+WAITING\n"
"report.waiting.sort=due+,wait+,entry+\n"
"\n"
"report.all.description=All tasks\n"
"report.all.labels=ID,St,UUID,A,Age,Done,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n"
"report.all.columns=id,status.short,uuid.short,start.active,entry.age,end.age,depends.indicator,priority,project.parent,tags.count,recur.indicator,wait.age,scheduled.age,due,until.age,description\n"
"report.all.columns=id,status.short,uuid.short,start.active,entry.age,end.age,depends.indicator,priority,project.parent,tags.count,recur.indicator,wait.remaining,scheduled.remaining,due,until.remaining,description\n"
"report.all.sort=entry-\n"
"\n"
"report.next.description=Most urgent tasks\n"
"report.next.labels=ID,Active,Age,Deps,P,Project,Tag,Recur,S,Due,Until,Description,Urg\n"
"report.next.columns=id,start.age,entry.age,depends,priority,project,tags,recur,scheduled.countdown,due.age,until.age,description,urgency\n"
"report.next.columns=id,start.age,entry.age,depends,priority,project,tags,recur,scheduled.countdown,due.remaining,until.remaining,description,urgency\n"
"report.next.filter=status:pending limit:page\n"
"report.next.sort=start-,urgency-\n"
"\n"
"report.ready.description=Most urgent actionable tasks\n"
"report.ready.labels=ID,Active,Age,D,P,Project,Tags,R,S,Due,Until,Description,Urg\n"
"report.ready.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur.indicator,scheduled.countdown,due.countdown,until.age,description,urgency\n"
"report.ready.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur.indicator,scheduled.countdown,due.countdown,until.remaining,description,urgency\n"
"report.ready.filter=+READY\n"
"report.ready.sort=start-,urgency-\n"
"\n"
@@ -453,7 +455,7 @@ std::string Config::_defaults =
"\n"
"report.blocking.description=Blocking tasks\n"
"report.blocking.labels=ID,UUID,A,Deps,Project,Tags,R,W,Sch,Due,Until,Description,Urg\n"
"report.blocking.columns=id,uuid.short,start.active,depends,project,tags,recur,wait.indicator,scheduled.age,due.age,until.age,description.count,urgency\n"
"report.blocking.columns=id,uuid.short,start.active,depends,project,tags,recur,wait.indicator,scheduled.remaining,due.remaining,until.remaining,description.count,urgency\n"
"report.blocking.sort=urgency-,due+,entry+\n"
"report.blocking.filter= status:pending +BLOCKING\n"
"\n";

View File

@@ -504,7 +504,7 @@ bool Context::color ()
use_color = config.getBoolean ("color");
// Only tty's support color.
if (! isatty (fileno (stdout)))
if (! isatty (STDOUT_FILENO))
{
// No ioctl.
config.set ("detection", "off");
@@ -658,6 +658,7 @@ void Context::staticInitialization ()
Task::searchCaseSensitive = Variant::searchCaseSensitive = config.getBoolean ("search.case.sensitive");
Task::regex = Variant::searchUsingRegex = config.getBoolean ("regex");
Lexer::dateFormat = Variant::dateFormat = config.get ("dateformat");
Lexer::isoEnabled = Variant::isoEnabled = config.getBoolean ("date.iso");
std::map <std::string, Column*>::iterator i;
for (i = columns.begin (); i != columns.end (); ++i)
@@ -770,7 +771,7 @@ void Context::clear ()
// this output?'.
void Context::updateXtermTitle ()
{
if (config.getBoolean ("xterm.title") && isatty (fileno (stdout)))
if (config.getBoolean ("xterm.title") && isatty (STDOUT_FILENO))
{
std::string command = cli.getCommand ();
std::string title;

View File

@@ -240,6 +240,12 @@ bool DOM::get (const std::string& name, const Task& task, Variant& value)
Column* column = context.columns[canonical];
if (column)
{
if (column->is_uda () && ! task.has (canonical))
{
value = Variant ("''");
return true;
}
if (column->type () == "date")
value = Variant (task.get_date (canonical), Variant::type_date);
else if (column->type () == "duration" || canonical == "recur")
@@ -307,6 +313,12 @@ bool DOM::get (const std::string& name, const Task& task, Variant& value)
Column* column = context.columns[canonical];
if (column)
{
if (column->is_uda () && ! task.has (canonical))
{
value = Variant ("''");
return true;
}
if (column->type () == "date")
value = Variant (ref.get_date (canonical), Variant::type_date);
else if (column->type () == "duration")

View File

@@ -38,8 +38,10 @@
#include <Context.h>
#include <JSON.h>
#include <Hooks.h>
#include <Timer.h>
#include <text.h>
#include <util.h>
#include <i18n.h>
extern Context context;
@@ -73,7 +75,17 @@ void Hooks::initialize ()
{
std::vector <std::string>::iterator i;
for (i = _scripts.begin (); i != _scripts.end (); ++i)
context.debug ("Found hook script " + *i);
{
Path p (*i);
std::string name = p.name ();
if (name.substr (0, 6) == "on-add" ||
name.substr (0, 9) == "on-modify" ||
name.substr (0, 9) == "on-launch" ||
name.substr (0, 7) == "on-exit")
context.debug ("Found hook script " + *i);
else
context.debug ("Found misnamed hook script " + *i);
}
}
}
else if (_debug >= 1)
@@ -98,8 +110,7 @@ bool Hooks::enable (bool value)
// - none
//
// Output:
// - all emitted JSON lines are added/modified as tasks, if the exit code is
// zero, otherwise ignored.
// - JSON not allowed.
// - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code.
//
@@ -120,29 +131,28 @@ void Hooks::onLaunch ()
std::vector <std::string> output;
int status = callHookScript (*script, input, output);
std::vector <std::string>::iterator line;
for (line = output.begin (); line != output.end (); ++line)
{
if (isJSON (*line))
{
if (status == 0)
{
// Only 'add' is possible.
Task newTask (*line);
context.tdb2.add (newTask);
}
}
else
{
if (status == 0)
context.header (*line);
else
context.error (*line);
}
}
std::vector <std::string> outputJSON;
std::vector <std::string> outputFeedback;
separateOutput (output, outputJSON, outputFeedback);
assertNTasks (outputJSON, 0);
if (status == 0)
{
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.debug (*message);
}
else
{
assertFeedback (outputFeedback);
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.error (*message);
if (status)
throw 0; // This is how hooks silently terminate processing.
}
}
}
@@ -157,7 +167,7 @@ void Hooks::onLaunch ()
// - read-only line of JSON for each task added/modified
//
// Output:
// - any emitted JSON is ignored
// - all emitted JSON is ignored
// - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code.
//
@@ -188,24 +198,28 @@ void Hooks::onExit ()
std::vector <std::string> output;
int status = callHookScript (*script, input, output);
std::vector <std::string>::iterator line;
for (line = output.begin (); line != output.end (); ++line)
{
if (isJSON (*line))
{
context.error ("JSON output ignored: {1}");
}
else
{
if (status == 0)
context.footnote (*line);
else
context.error (*line);
}
}
std::vector <std::string> outputJSON;
std::vector <std::string> outputFeedback;
separateOutput (output, outputJSON, outputFeedback);
assertNTasks (outputJSON, 0);
if (status == 0)
{
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.debug (*message);
}
else
{
assertFeedback (outputFeedback);
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.error (*message);
if (status)
throw 0; // This is how hooks silently terminate processing.
}
}
}
@@ -219,14 +233,14 @@ void Hooks::onExit ()
// - line of JSON for the task added
//
// Output:
// - all emitted JSON lines are added/modified as tasks, if the exit code is
// zero, otherwise ignored.
// - emitted JSON for the input task is added, if the exit code is zero,
// otherwise ignored.
// - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code.
//
void Hooks::onAdd (std::vector <Task>& tasks)
void Hooks::onAdd (Task& task)
{
if (! _enabled || tasks.size () < 1)
if (! _enabled)
return;
context.timer_hooks.start ();
@@ -234,9 +248,9 @@ void Hooks::onAdd (std::vector <Task>& tasks)
std::vector <std::string> matchingScripts = scripts ("on-add");
if (matchingScripts.size ())
{
// Convert vector of tasks to a vector of strings.
// Convert task to a vector of strings.
std::vector <std::string> input;
input.push_back (tasks[0].composeJSON ());
input.push_back (task.composeJSON ());
// Call the hook scripts.
std::vector <std::string>::iterator script;
@@ -245,33 +259,37 @@ void Hooks::onAdd (std::vector <Task>& tasks)
std::vector <std::string> output;
int status = callHookScript (*script, input, output);
input.clear ();
std::vector <std::string>::iterator line;
for (line = output.begin (); line != output.end (); ++line)
{
if (isJSON (*line))
{
if (status == 0)
input.push_back (*line);
}
else
{
if (status == 0)
context.footnote (*line);
else
context.error (*line);
}
}
std::vector <std::string> outputJSON;
std::vector <std::string> outputFeedback;
separateOutput (output, outputJSON, outputFeedback);
if (status == 0)
{
assertNTasks (outputJSON, 1);
assertValidJSON (outputJSON);
assertSameTask (outputJSON, task);
// Propagate forward to the next script.
input[0] = outputJSON[0];
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.debug (*message);
}
else
{
assertFeedback (outputFeedback);
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.error (*message);
if (status)
throw 0; // This is how hooks silently terminate processing.
}
}
// Transfer the modified task lines back to the original task list.
tasks.clear ();
std::vector <std::string>::iterator i;
for (i = input.begin (); i != input.end (); ++i)
tasks.push_back (Task (*i));
// Transfer the modified task back to the original task.
task = Task (input[0]);
}
context.timer_hooks.stop ();
@@ -285,14 +303,14 @@ void Hooks::onAdd (std::vector <Task>& tasks)
// - line of JSON for the modified task, the diff being the modification
//
// Output:
// - all emitted JSON lines are added/modified as tasks, if the exit code is
// zero, otherwise ignored.
// - emitted JSON for the input task is saved, if the exit code is zero,
// otherwise ignored.
// - all emitted non-JSON lines are considered feedback or error messages
// depending on the status code.
//
void Hooks::onModify (const Task& before, std::vector <Task>& tasks)
void Hooks::onModify (const Task& before, Task& after)
{
if (! _enabled || tasks.size () < 1)
if (! _enabled)
return;
context.timer_hooks.start ();
@@ -300,61 +318,46 @@ void Hooks::onModify (const Task& before, std::vector <Task>& tasks)
std::vector <std::string> matchingScripts = scripts ("on-modify");
if (matchingScripts.size ())
{
// Prepare invariants.
std::string beforeJSON = before.composeJSON ();
std::string uuidPattern = "\"uuid\":\"" + before.get ("uuid") + "\"";
// Convert vector of tasks to a vector of strings.
std::vector <std::string> input;
input.push_back (beforeJSON); // [0] original, never changes
input.push_back (tasks[0].composeJSON ()); // [1] original'
input.push_back (before.composeJSON ()); // [line 0] original, never changes
input.push_back (after.composeJSON ()); // [line 1] modified
// Call the hook scripts.
std::vector <std::string>::iterator script;
for (script = matchingScripts.begin (); script != matchingScripts.end (); ++script)
{
std::vector <std::string> firstTwoOnly;
firstTwoOnly.push_back (input[0]);
firstTwoOnly.push_back (input[1]);
std::vector <std::string> output;
int status = callHookScript (*script, firstTwoOnly, output);
int status = callHookScript (*script, input, output);
// Start from scratch.
input[1] = ""; // [1] placeholder for original'
std::vector <std::string> outputJSON;
std::vector <std::string> outputFeedback;
separateOutput (output, outputJSON, outputFeedback);
std::vector <std::string>::iterator line;
for (line = output.begin (); line != output.end (); ++line)
if (status == 0)
{
if (isJSON (*line))
{
if (status == 0)
{
if (line->find (uuidPattern) != std::string::npos)
input[1] = *line; // [1] original'
else
input.push_back (*line); // [n > 1] extras
}
}
else
{
if (status == 0)
context.footnote (*line);
else
context.error (*line);
}
assertNTasks (outputJSON, 1);
assertValidJSON (outputJSON);
assertSameTask (outputJSON, before);
// Propagate accepted changes forward to the next script.
input[1] = outputJSON[0];
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.debug (*message);
}
else
{
assertFeedback (outputFeedback);
std::vector <std::string>::iterator message;
for (message = outputFeedback.begin (); message != outputFeedback.end (); ++message)
context.error (*message);
if (status)
throw 0; // This is how hooks silently terminate processing.
}
}
// Transfer the modified task lines back to the original task list.
tasks.clear ();
std::vector <std::string>::iterator i;
for (i = input.begin (); i != input.end (); ++i)
if (i != input.begin ())
tasks.push_back (Task (*i));
}
context.timer_hooks.stop ();
@@ -384,65 +387,135 @@ std::vector <std::string> Hooks::scripts (const std::string& event)
return matching;
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::separateOutput (
const std::vector <std::string>& output,
std::vector <std::string>& json,
std::vector <std::string>& feedback) const
{
std::vector <std::string>::const_iterator i;
for (i = output.begin (); i != output.end (); ++i)
{
if (isJSON (*i))
json.push_back (*i);
else
feedback.push_back (*i);
}
}
////////////////////////////////////////////////////////////////////////////////
bool Hooks::isJSON (const std::string& input) const
{
// Does it even look like JSON? {...}
if (input.length () > 2 &&
input[0] == '{' &&
input[input.length () - 1] == '}')
return input.length () > 2 &&
input[0] == '{' &&
input[input.length () - 1] == '}';
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertValidJSON (const std::vector <std::string>& input) const
{
std::vector <std::string>::const_iterator i;
for (i = input.begin (); i != input.end (); i++)
{
if (i->length () < 3 ||
(*i)[0] != '{' ||
(*i)[i->length () - 1] != '}')
{
context.error (STRING_HOOK_ERROR_OBJECT);
throw 0;
}
try
{
// The absolute minimum a task needs is:
bool foundDescription = false;
// Parse the whole thing.
json::value* root = json::parse (input);
if (root->type () == json::j_object)
json::value* root = json::parse (*i);
if (root->type () != json::j_object)
{
json::object* root_obj = (json::object*)root;
// For each object element...
json_object_iter i;
for (i = root_obj->_data.begin ();
i != root_obj->_data.end ();
++i)
{
// If the attribute is a recognized column.
std::string type = Task::attributes[i->first];
if (type == "string" && i->first == "description")
foundDescription = true;
}
context.error (STRING_HOOK_ERROR_OBJECT);
throw 0;
}
else
throw std::string ("Object expected.");
// It's JSON, but is it a task?
if (! foundDescription)
throw std::string ("Missing 'description' attribute, of type 'string'.");
if (((json::object*)root)->_data.find ("description") == ((json::object*)root)->_data.end ())
{
context.error (STRING_HOOK_ERROR_NODESC);
throw 0;
}
// Yep, looks like a JSON task.
return true;
if (((json::object*)root)->_data.find ("uuid") == ((json::object*)root)->_data.end ())
{
context.error (STRING_HOOK_ERROR_NOUUID);
throw 0;
}
}
catch (const std::string& e)
{
if (_debug >= 1)
context.error ("Hook output looks like JSON, but is not a valid task.");
if (_debug >= 2)
context.error ("JSON " + e);
context.error (format (STRING_HOOK_ERROR_SYNTAX, *i));
if (_debug)
context.error (STRING_HOOK_ERROR_JSON + e);
throw 0;
}
catch (...)
{
if (_debug >= 1)
context.error ("Hook output looks like JSON, but fails to parse.");
context.error (STRING_HOOK_ERROR_NOPARSE + *i);
throw 0;
}
}
}
return false;
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertNTasks (const std::vector <std::string>& input, int n) const
{
if (input.size () != n)
{
context.error (format (STRING_HOOK_ERROR_BAD_NUM, n, (int) input.size ()));
throw 0;
}
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertSameTask (const std::vector <std::string>& input, const Task& task) const
{
std::string uuid = task.get ("uuid");
std::vector <std::string>::const_iterator i;
for (i = input.begin (); i != input.end (); i++)
{
json::object* root_obj = (json::object*)json::parse (*i);
// If there is no UUID at all.
json_object_iter u = root_obj->_data.find ("uuid");
if (u == root_obj->_data.end () ||
u->second->type () != json::j_string)
{
context.error (format (STRING_HOOK_ERROR_SAME1, uuid));
throw 0;
}
json::string* up = (json::string*) u->second;
std::string json_uuid = json::decode (unquoteText (up->dump ()));
if (json_uuid != uuid)
{
context.error (format (STRING_HOOK_ERROR_SAME2, uuid, json_uuid));
throw 0;
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertFeedback (const std::vector <std::string>& input) const
{
bool foundSomething = false;
std::vector <std::string>::const_iterator i;
for (i = input.begin (); i != input.end (); ++i)
if (nontrivial (*i))
foundSomething = true;
if (! foundSomething)
{
context.error (STRING_HOOK_ERROR_NOFEEDBACK);
throw 0;
}
}
////////////////////////////////////////////////////////////////////////////////
@@ -452,11 +525,11 @@ int Hooks::callHookScript (
std::vector <std::string>& output)
{
if (_debug >= 1)
context.debug ("Hooks: Calling " + script);
context.debug ("Hook: Calling " + script);
if (_debug >= 2)
{
context.debug ("Hooks: input");
context.debug ("Hook: input");
std::vector <std::string>::const_iterator i;
for (i = input.begin (); i != input.end (); ++i)
context.debug (" " + *i);
@@ -469,19 +542,30 @@ int Hooks::callHookScript (
std::string outputStr;
std::vector <std::string> args;
int status = execute (script, args, inputStr, outputStr);
int status;
// Measure time for each hook if running in debug
if (_debug >= 2)
{
Timer timer_per_hook("Hooks::execute (" + script + ")");
timer_per_hook.start();
status = execute (script, args, inputStr, outputStr);
}
else
status = execute (script, args, inputStr, outputStr);
split (output, outputStr, '\n');
if (_debug >= 2)
{
context.debug ("Hooks: output");
context.debug ("Hook: output");
std::vector <std::string>::iterator i;
for (i = output.begin (); i != output.end (); ++i)
if (*i != "")
context.debug (" " + *i);
context.debug (format ("Hooks: Completed with status {1}", status));
context.debug (format ("Hook: Completed with status {1}", status));
context.debug (" "); // Blank line
}

View File

@@ -44,14 +44,19 @@ public:
void onLaunch ();
void onExit ();
void onAdd (std::vector <Task>&);
void onModify (const Task&, std::vector <Task>&);
void onAdd (Task&);
void onModify (const Task&, Task&);
std::vector <std::string> list ();
private:
std::vector <std::string> scripts (const std::string&);
void separateOutput (const std::vector <std::string>&, std::vector <std::string>&, std::vector <std::string>&) const;
bool isJSON (const std::string&) const;
void assertValidJSON (const std::vector <std::string>&) const;
void assertNTasks (const std::vector <std::string>&, int) const;
void assertSameTask (const std::vector <std::string>&, const Task&) const;
void assertFeedback (const std::vector <std::string>&) const;
int callHookScript (const std::string&, const std::vector <std::string>&, std::vector <std::string>&);
private:

View File

@@ -702,7 +702,7 @@ void ISO8601d::resolve ()
t.tm_min = (seconds % 3600) / 60;
t.tm_sec = seconds % 60;
_value = utc ? timegm (&t) : timelocal (&t);
_value = utc ? timegm (&t) : mktime (&t);
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -34,6 +34,7 @@
#include <i18n.h>
std::string Lexer::dateFormat = "";
bool Lexer::isoEnabled = true;
////////////////////////////////////////////////////////////////////////////////
Lexer::Lexer (const std::string& input)
@@ -732,15 +733,18 @@ void Lexer::dequote (std::string& input)
bool Lexer::is_date (std::string& result)
{
// Try an ISO date parse.
std::string::size_type iso_i = 0;
std::string iso_result;
ISO8601d iso;
iso.ambiguity (_ambiguity);
if (iso.parse (_input.substr (_shift_counter), iso_i))
if (isoEnabled)
{
result = _input.substr (_shift_counter, iso_i);
while (iso_i--) shift ();
return true;
std::string::size_type iso_i = 0;
std::string iso_result;
ISO8601d iso;
iso.ambiguity (_ambiguity);
if (iso.parse (_input.substr (_shift_counter), iso_i))
{
result = _input.substr (_shift_counter, iso_i);
while (iso_i--) shift ();
return true;
}
}
// Try a legacy rc.dateformat parse here.

View File

@@ -34,6 +34,7 @@ class Lexer
{
public:
static std::string dateFormat;
static bool isoEnabled;
enum Type
{

View File

@@ -567,12 +567,12 @@ void TDB2::add (Task& task, bool add_to_backlog /* = true */)
if (!verifyUniqueUUID (uuid))
throw format (STRING_TDB2_UUID_NOT_UNIQUE, uuid);
// Create a vector tasks, as hooks can cause them to multiply.
std::vector <Task> changes;
changes.push_back (task);
context.hooks.onAdd (changes);
// Only locally-added tasks trigger hooks. This means that tasks introduced
// via 'sync' do not trigger hooks.
if (add_to_backlog)
context.hooks.onAdd (task);
update (uuid, task, add_to_backlog, changes);
update (uuid, task, add_to_backlog);
}
////////////////////////////////////////////////////////////////////////////////
@@ -584,77 +584,64 @@ void TDB2::modify (Task& task, bool add_to_backlog /* = true */)
std::string uuid = task.get ("uuid");
// Get the unmodified task as reference, so the hook can compare.
Task original;
get (uuid, original);
if (add_to_backlog)
{
Task original;
get (uuid, original);
context.hooks.onModify (original, task);
}
// Create a vector tasks, as hooks can cause them to multiply.
std::vector <Task> changes;
changes.push_back (task);
context.hooks.onModify (original, changes);
update (uuid, task, add_to_backlog, changes);
update (uuid, task, add_to_backlog);
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::update (
const std::string& uuid,
Task& task,
const bool add_to_backlog,
std::vector <Task>& changes)
const bool add_to_backlog)
{
std::vector <Task>::iterator i;
for (i = changes.begin (); i != changes.end (); ++i)
// Validate to add metadata.
task.validate (false);
// If the task already exists, it is a modification, else addition.
Task original;
if (get (task.get ("uuid"), original))
{
// Validate to add metadata.
i->validate (false);
// Update the task, wherever it is.
if (!pending.modify_task (task))
completed.modify_task (task);
// If the task already exists, it is a modification, else addition.
Task original;
if (get (i->get ("uuid"), original))
{
// Update the task, wherever it is.
if (!pending.modify_task (*i))
completed.modify_task (*i);
// time <time>
// old <task>
// new <task>
// ---
undo.add_line ("time " + Date ().toEpochString () + "\n");
undo.add_line ("old " + original.composeF4 () + "\n");
undo.add_line ("new " + i->composeF4 () + "\n");
undo.add_line ("---\n");
}
else
{
// Re-validate to add default values.
i->validate ();
// Add new task to either pending or completed.
std::string status = i->get ("status");
if (status == "completed" ||
status == "deleted")
completed.add_task (*i);
else
pending.add_task (*i);
// Add undo data lines:
// time <time>
// new <task>
// ---
undo.add_line ("time " + Date ().toEpochString () + "\n");
undo.add_line ("new " + i->composeF4 () + "\n");
undo.add_line ("---\n");
}
// Add task to backlog.
if (add_to_backlog)
backlog.add_line (i->composeJSON () + "\n");
// The original task may be further referenced by the caller.
if (i->get ("uuid") == uuid)
task = *i;
// time <time>
// old <task>
// new <task>
// ---
undo.add_line ("time " + Date ().toEpochString () + "\n");
undo.add_line ("old " + original.composeF4 () + "\n");
undo.add_line ("new " + task.composeF4 () + "\n");
undo.add_line ("---\n");
}
else
{
// Add new task to either pending or completed.
std::string status = task.get ("status");
if (status == "completed" ||
status == "deleted")
completed.add_task (task);
else
pending.add_task (task);
// Add undo data lines:
// time <time>
// new <task>
// ---
undo.add_line ("time " + Date ().toEpochString () + "\n");
undo.add_line ("new " + task.composeF4 () + "\n");
undo.add_line ("---\n");
}
// Add task to backlog.
if (add_to_backlog)
backlog.add_line (task.composeJSON () + "\n");
}
////////////////////////////////////////////////////////////////////////////////
@@ -979,6 +966,9 @@ void TDB2::show_diff (
view.add (Column::factory ("string", STRING_TDB2_UNDO_PRIOR));
view.add (Column::factory ("string", STRING_TDB2_UNDO_CURRENT));
Color label (context.config.get ("color.label"));
view.colorHeader (label);
Task after (current);
if (prior != "")

View File

@@ -128,7 +128,7 @@ public:
private:
void gather_changes ();
void update (const std::string&, Task&, const bool, std::vector <Task>&);
void update (const std::string&, Task&, const bool);
bool verifyUniqueUUID (const std::string&);
void show_diff (const std::string&, const std::string&, const std::string&);
void revert_undo (std::vector <std::string>&, std::string&, std::string&, std::string&, std::string&);

View File

@@ -636,8 +636,9 @@ void Task::parseJSON (const std::string& line)
// Dates are converted from ISO to epoch.
else if (type == "date")
{
Date d (unquoteText (i->second->dump ()));
set (i->first, d.toEpochString ());
std::string text = unquoteText (i->second->dump ());
Date d (text);
set (i->first, text == "" ? "" : d.toEpochString ());
}
// Tags are an array of JSON strings.
@@ -804,6 +805,10 @@ std::string Task::composeJSON (bool decorate /*= false*/) const
if (i->first.substr (0, 11) == "annotation_")
continue;
// If value is an empty string, do not ever output it
if (i->second == "")
continue;
if (attributes_written)
out << ",";
@@ -815,16 +820,22 @@ std::string Task::composeJSON (bool decorate /*= false*/) const
if (type == "date")
{
Date d (i->second);
if (i->first == "modification")
out << "\"modified\":\""
<< d.toISO ()
<< "\"";
else
out << "\""
<< i->first
<< "\":\""
<< d.toISO ()
<< "\"";
out << "\""
<< (i->first == "modification" ? "modified" : i->first)
<< "\":\""
// Date was deleted, do not export parsed empty string
<< (i->second == "" ? "" : d.toISO ())
<< "\"";
++attributes_written;
}
else if (type == "numeric")
{
out << "\""
<< i->first
<< "\":"
<< i->second;
++attributes_written;
}
@@ -847,6 +858,8 @@ std::string Task::composeJSON (bool decorate /*= false*/) const
}
out << "]";
++attributes_written;
}
// Everything else is a quoted value.
@@ -894,9 +907,8 @@ std::string Task::composeJSON (bool decorate /*= false*/) const
// Include urgency.
if (decorate)
out << ","
<< "\"urgency\":\""
<< urgency_c ()
<<"\"";
<< "\"urgency\":"
<< urgency_c ();
#endif
out << "}";
@@ -1112,10 +1124,10 @@ bool Task::hasTag (const std::string& tag) const
if (tag == "SCHEDULED") return has ("scheduled");
if (tag == "CHILD") return has ("parent");
if (tag == "UNTIL") return has ("until");
if (tag == "WAITING") return has ("wait");
if (tag == "ANNOTATED") return hasAnnotations ();
if (tag == "TAGGED") return has ("tags");
if (tag == "PARENT") return has ("mask");
if (tag == "WAITING") return get ("status") == "waiting";
if (tag == "PENDING") return get ("status") == "pending";
if (tag == "COMPLETED") return get ("status") == "completed";
if (tag == "DELETED") return get ("status") == "deleted";
@@ -2068,9 +2080,6 @@ void Task::modify (modType type, bool text_required /* = false */)
if (v.type () == Variant::type_string)
throw format (STRING_UDA_NUMERIC, v.get_string ());
v.cast (Variant::type_real);
v.cast (Variant::type_string);
set (name, v);
++modCount;
}

View File

@@ -41,6 +41,7 @@
std::string Variant::dateFormat = "";
bool Variant::searchCaseSensitive = true;
bool Variant::searchUsingRegex = true;
bool Variant::isoEnabled = true;
////////////////////////////////////////////////////////////////////////////////
Variant::Variant ()
@@ -2065,7 +2066,8 @@ void Variant::cast (const enum type new_type)
ISO8601d iso;
std::string::size_type pos = 0;
if (iso.parse (_string, pos) &&
if (isoEnabled &&
iso.parse (_string, pos) &&
pos == _string.length ())
{
_date = (time_t) iso;

View File

@@ -37,6 +37,7 @@ public:
static std::string dateFormat;
static bool searchCaseSensitive;
static bool searchUsingRegex;
static bool isoEnabled;
enum type {type_unknown, type_boolean, type_integer, type_real, type_string,
type_date, type_duration};

View File

@@ -57,7 +57,7 @@ int main (int argc, char** argv)
try
{
bool infix = true;
bool ambiguous = true;
bool ambiguous = false;
// Add a source for constants.
Eval e;
@@ -77,8 +77,8 @@ int main (int argc, char** argv)
<< " -d|--debug Debug mode\n"
<< " -i|--infix Infix expression (default)\n"
<< " -p|--postfix Postfix expression\n"
<< " -a|--ambiguous Choose dates over numbers when ambiguous (default)\n"
<< " -n|--noambiguous Choose numbers over dates when ambiguous\n"
<< " -a|--ambiguous Choose dates over numbers when ambiguous\n"
<< " -n|--noambiguous Choose numbers over dates when ambiguous (default)\n"
<< "\n";
exit (1);
}

View File

@@ -37,7 +37,7 @@ class ColumnDate : public Column
{
public:
ColumnDate ();
~ColumnDate ();
virtual ~ColumnDate ();
virtual bool validate (std::string&);
virtual void measure (Task&, unsigned int&, unsigned int&);

View File

@@ -284,6 +284,7 @@ int CmdCalendar::execute (std::string& output)
Color color_weekend (context.config.get ("color.calendar.weekend"));
Color color_holiday (context.config.get ("color.calendar.holiday"));
Color color_weeknumber (context.config.get ("color.calendar.weeknumber"));
Color color_label (context.config.get ("color.label"));
if (context.color () && context.config.getBoolean ("calendar.legend"))
out << "Legend: "
@@ -342,7 +343,6 @@ int CmdCalendar::execute (std::string& output)
executable = executable.substr (0, cal) + PACKAGE;
std::vector <std::string> args;
args.push_back (executable);
args.push_back ("rc:" + context.rc_file._data);
args.push_back ("rc.due:0");
args.push_back ("rc.verbose:label,affected,blank");
@@ -363,6 +363,7 @@ int CmdCalendar::execute (std::string& output)
holTable.width (context.getWidth ());
holTable.add (Column::factory ("string", STRING_CMD_CAL_LABEL_DATE));
holTable.add (Column::factory ("string", STRING_CMD_CAL_LABEL_HOL));
holTable.colorHeader (color_label);
Config::const_iterator it;
std::map <time_t, std::vector<std::string> > hm; // we need to store multiple holidays per day
@@ -426,7 +427,10 @@ std::string CmdCalendar::renderMonths (
throw std::string (STRING_CMD_CAL_SUN_MON);
// Build table for the number of months to be displayed.
Color label (context.config.get ("color.label"));
ViewText view;
view.colorHeader (label);
view.width (context.getWidth ());
for (int i = 0 ; i < (monthsPerLine * 8); i += 8)
{

View File

@@ -70,6 +70,9 @@ int CmdColumns::execute (std::string& output)
formats.add (Column::factory ("string", STRING_COLUMN_LABEL_STYLES));
formats.add (Column::factory ("string", STRING_COLUMN_LABEL_EXAMPLES));
Color label (context.config.get ("color.label"));
formats.colorHeader (label);
Color alternate (context.config.get ("color.alternate"));
formats.colorOdd (alternate);
formats.intraColorOdd (alternate);

View File

@@ -312,7 +312,7 @@ int CmdDiagnostics::execute (std::string& output)
out << bold.colorize (STRING_CMD_DIAG_HOOKS)
<< "\n"
<< " Scripts: "
<< (context.config.getBoolean ("hooks") ? "Enabled" : "Disabled")
<< (context.config.getBoolean ("hooks") ? STRING_CMD_DIAG_HOOK_ENABLE : STRING_CMD_DIAG_HOOK_DISABLE)
<< "\n";
std::vector <std::string> hooks = context.hooks.list ();
@@ -322,15 +322,20 @@ int CmdDiagnostics::execute (std::string& output)
for (h = hooks.begin (); h != hooks.end (); ++h)
{
Path p (*h);
std::string name = p.name ();
out << " "
<< *h
<< (p.executable () ? " (executable)" : " (not executable)")
<< (p.is_link () ? " (symlink)" : "")
<< (p.executable () ? format (" ({1})", STRING_CMD_DIAG_HOOK_EXEC) : format (" ({1})", STRING_CMD_DIAG_HOOK_NO_EXEC))
<< (p.is_link () ? format (" ({1})", STRING_CMD_DIAG_HOOK_SYMLINK) : "")
<< ((name.substr (0, 6) == "on-add" ||
name.substr (0, 9) == "on-modify" ||
name.substr (0, 9) == "on-launch" ||
name.substr (0, 7) == "on-exit") ? "" : format (" ({1})", STRING_CMD_DIAG_HOOK_NAME))
<< "\n";
}
}
else
out << " (none)\n";
out << format (" ({1})\n", STRING_CMD_DIAG_NONE);
out << "\n";

View File

@@ -48,6 +48,9 @@ int CmdExport::execute (std::string& output)
{
int rc = 0;
// Make sure reccurent tasks are generated.
handleRecurrence ();
// Apply filter.
Filter filter;
std::vector <Task> filtered;

View File

@@ -104,6 +104,9 @@ int CmdHistoryMonthly::execute (std::string& output)
view.add (Column::factory ("string.right", STRING_CMD_HISTORY_DEL));
view.add (Column::factory ("string.right", STRING_CMD_HISTORY_NET));
Color label (context.config.get ("color.label"));
view.colorHeader (label);
int totalAdded = 0;
int totalCompleted = 0;
int totalDeleted = 0;
@@ -257,6 +260,9 @@ int CmdHistoryAnnual::execute (std::string& output)
view.add (Column::factory ("string.right", STRING_CMD_HISTORY_DEL));
view.add (Column::factory ("string.right", STRING_CMD_HISTORY_NET));
Color label (context.config.get ("color.label"));
view.colorHeader (label);
int totalAdded = 0;
int totalCompleted = 0;
int totalDeleted = 0;
@@ -411,6 +417,9 @@ int CmdGHistoryMonthly::execute (std::string& output)
Color color_add (context.config.get ("color.history.add"));
Color color_done (context.config.get ("color.history.done"));
Color color_delete (context.config.get ("color.history.delete"));
Color label (context.config.get ("color.label"));
view.colorHeader (label);
// Determine the longest line, and the longest "added" line.
int maxAddedLine = 0;
@@ -603,6 +612,9 @@ int CmdGHistoryAnnual::execute (std::string& output)
Color color_add (context.config.get ("color.history.add"));
Color color_done (context.config.get ("color.history.done"));
Color color_delete (context.config.get ("color.history.delete"));
Color label (context.config.get ("color.label"));
view.colorHeader (label);
// Determine the longest line, and the longest "added" line.
int maxAddedLine = 0;

View File

@@ -32,7 +32,6 @@
#include <Filter.h>
#include <Date.h>
#include <Duration.h>
#include <ViewText.h>
#include <main.h>
#include <text.h>
#include <i18n.h>
@@ -106,6 +105,9 @@ int CmdInfo::execute (std::string& output)
Color alternate (context.config.get ("color.alternate"));
view.colorOdd (alternate);
view.intraColorOdd (alternate);
Color label (context.config.get ("color.label"));
view.colorHeader (label);
}
Date now;
@@ -321,62 +323,7 @@ int CmdInfo::execute (std::string& output)
// Task::urgency
row = view.addRow ();
view.set (row, 0, STRING_COLUMN_LABEL_URGENCY);
std::string urgency =
trimLeft (format (task->urgency (), 4, 4)) + "\n" +
urgencyTerm ("project", task->urgency_project (), Task::urgencyProjectCoefficient) +
urgencyTerm ("priority", task->urgency_priority (), Task::urgencyPriorityCoefficient) +
urgencyTerm ("active", task->urgency_active (), Task::urgencyActiveCoefficient) +
urgencyTerm ("scheduled", task->urgency_scheduled (), Task::urgencyScheduledCoefficient) +
urgencyTerm ("waiting", task->urgency_waiting (), Task::urgencyWaitingCoefficient) +
urgencyTerm ("blocked", task->urgency_blocked (), Task::urgencyBlockedCoefficient) +
urgencyTerm ("blocking", task->urgency_blocking (), Task::urgencyBlockingCoefficient) +
urgencyTerm ("annotations", task->urgency_annotations (), Task::urgencyAnnotationsCoefficient) +
urgencyTerm ("tags", task->urgency_tags (), Task::urgencyTagsCoefficient) +
urgencyTerm ("next", task->urgency_next (), Task::urgencyNextCoefficient) +
urgencyTerm ("due", task->urgency_due (), Task::urgencyDueCoefficient) +
urgencyTerm ("age", task->urgency_age (), Task::urgencyAgeCoefficient);
// Tag, Project- and UDA-specific coefficients.
std::map <std::string, float>::iterator var;
for (var = Task::coefficients.begin (); var != Task::coefficients.end (); ++var)
{
if (var->first.substr (0, 13) == "urgency.user.")
{
// urgency.user.project.<project>.coefficient
std::string::size_type end = std::string::npos;
if (var->first.substr (13, 8) == "project." &&
(end = var->first.find (".coefficient")) != std::string::npos)
{
std::string project = var->first.substr (21, end - 21);
if (task->get ("project").find (project) == 0)
urgency += urgencyTerm ("PROJECT " + project, 1.0, var->second);
}
// urgency.user.tag.<tag>.coefficient
if (var->first.substr (13, 4) == "tag." &&
(end = var->first.find (".coefficient")) != std::string::npos)
{
std::string name = var->first.substr (17, end - 17);
if (task->hasTag (name))
urgency += urgencyTerm ("TAG " + name, 1.0, var->second);
}
}
// urgency.uda.<name>.coefficient
else if (var->first.substr (0, 12) == "urgency.uda.")
{
std::string::size_type end = var->first.find (".coefficient");
if (end != std::string::npos)
{
std::string name = var->first.substr (12, end - 12);
if (task->has (name))
urgency += urgencyTerm ("UDA " + name, 1.0, var->second);
}
}
}
view.set (row, 1, urgency);
view.set (row, 1, format (task->urgency (), 4, 4));
// Show any UDAs
std::vector <std::string> all = task->all ();
@@ -410,6 +357,7 @@ int CmdInfo::execute (std::string& output)
// Show any orphaned UDAs, which are identified by not being represented in
// the context.columns map.
for (att = all.begin (); att != all.end (); ++att)
{
if (att->substr (0, 11) != "annotation_" &&
context.columns.find (*att) == context.columns.end ())
{
@@ -417,8 +365,86 @@ int CmdInfo::execute (std::string& output)
view.set (row, 0, "[" + *att);
view.set (row, 1, task->get (*att) + "]");
}
}
// Create a second table, containing undo log change details.
// Create a second table, containing urgency details.
ViewText urgencyDetails;
if (context.color ())
{
Color alternate (context.config.get ("color.alternate"));
urgencyDetails.colorOdd (alternate);
urgencyDetails.intraColorOdd (alternate);
Color label (context.config.get ("color.label"));
urgencyDetails.colorHeader (label);
}
urgencyDetails.width (context.getWidth ());
urgencyDetails.add (Column::factory ("string", "")); // Attribute
urgencyDetails.add (Column::factory ("string", "")); // Value
urgencyDetails.add (Column::factory ("string", "")); // *
urgencyDetails.add (Column::factory ("string", "")); // Coefficient
urgencyDetails.add (Column::factory ("string", "")); // =
urgencyDetails.add (Column::factory ("string", "")); // Result
urgencyTerm (urgencyDetails, "project", task->urgency_project (), Task::urgencyProjectCoefficient);
urgencyTerm (urgencyDetails, "priority", task->urgency_priority (), Task::urgencyPriorityCoefficient);
urgencyTerm (urgencyDetails, "active", task->urgency_active (), Task::urgencyActiveCoefficient);
urgencyTerm (urgencyDetails, "scheduled", task->urgency_scheduled (), Task::urgencyScheduledCoefficient);
urgencyTerm (urgencyDetails, "waiting", task->urgency_waiting (), Task::urgencyWaitingCoefficient);
urgencyTerm (urgencyDetails, "blocked", task->urgency_blocked (), Task::urgencyBlockedCoefficient);
urgencyTerm (urgencyDetails, "blocking", task->urgency_blocking (), Task::urgencyBlockingCoefficient);
urgencyTerm (urgencyDetails, "annotations", task->urgency_annotations (), Task::urgencyAnnotationsCoefficient);
urgencyTerm (urgencyDetails, "tags", task->urgency_tags (), Task::urgencyTagsCoefficient);
urgencyTerm (urgencyDetails, "next", task->urgency_next (), Task::urgencyNextCoefficient);
urgencyTerm (urgencyDetails, "due", task->urgency_due (), Task::urgencyDueCoefficient);
urgencyTerm (urgencyDetails, "age", task->urgency_age (), Task::urgencyAgeCoefficient);
// Tag, Project- and UDA-specific coefficients.
std::map <std::string, float>::iterator var;
for (var = Task::coefficients.begin (); var != Task::coefficients.end (); ++var)
{
if (var->first.substr (0, 13) == "urgency.user.")
{
// urgency.user.project.<project>.coefficient
std::string::size_type end = std::string::npos;
if (var->first.substr (13, 8) == "project." &&
(end = var->first.find (".coefficient")) != std::string::npos)
{
std::string project = var->first.substr (21, end - 21);
if (task->get ("project").find (project) == 0)
urgencyTerm (urgencyDetails, "PROJECT " + project, 1.0, var->second);
}
// urgency.user.tag.<tag>.coefficient
if (var->first.substr (13, 4) == "tag." &&
(end = var->first.find (".coefficient")) != std::string::npos)
{
std::string name = var->first.substr (17, end - 17);
if (task->hasTag (name))
urgencyTerm (urgencyDetails, "TAG " + name, 1.0, var->second);
}
}
// urgency.uda.<name>.coefficient
else if (var->first.substr (0, 12) == "urgency.uda.")
{
std::string::size_type end = var->first.find (".coefficient");
if (end != std::string::npos)
{
std::string name = var->first.substr (12, end - 12);
if (task->has (name))
urgencyTerm (urgencyDetails, "UDA " + name, 1.0, var->second);
}
}
}
row = urgencyDetails.addRow ();
urgencyDetails.set (row, 5, rightJustify ("------", 6));
row = urgencyDetails.addRow ();
urgencyDetails.set (row, 5, rightJustify (format (task->urgency (), 4, 4), 6));
// Create a third table, containing undo log change details.
ViewText journal;
// If an alternating row color is specified, notify the table.
@@ -427,6 +453,9 @@ int CmdInfo::execute (std::string& output)
Color alternate (context.config.get ("color.alternate"));
journal.colorOdd (alternate);
journal.intraColorOdd (alternate);
Color label (context.config.get ("color.label"));
journal.colorHeader (label);
}
journal.width (context.getWidth ());
@@ -473,6 +502,10 @@ int CmdInfo::execute (std::string& output)
<< view.render ()
<< "\n";
if (urgencyDetails.rows () > 0)
out << urgencyDetails.render ()
<< "\n";
if (journal.rows () > 0)
out << journal.render ()
<< "\n";
@@ -483,24 +516,23 @@ int CmdInfo::execute (std::string& output)
}
////////////////////////////////////////////////////////////////////////////////
std::string CmdInfo::urgencyTerm (
void CmdInfo::urgencyTerm (
ViewText& view,
const std::string& label,
float measure,
float coefficient) const
{
float value = measure * coefficient;
if (fabsf (value) > epsilon)
return std::string (
rightJustify (label, 20) +
" " +
leftJustify (format (measure, 5, 3), 6) +
" * " +
leftJustify (format (coefficient, 4, 2), 4) +
" = " +
leftJustify (format (value, 5, 3), 5) +
"\n");
return "";
if (value != 0.0)
{
int row = view.addRow ();
view.set (row, 0, " " + label);
view.set (row, 1, rightJustify (format (measure, 5, 3), 6));
view.set (row, 2, "+");
view.set (row, 3, rightJustify (format (coefficient, 4, 2), 4));
view.set (row, 4, "=");
view.set (row, 5, rightJustify (format (value, 5, 3), 6));
}
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -29,6 +29,7 @@
#include <string>
#include <Command.h>
#include <ViewText.h>
class CmdInfo : public Command
{
@@ -37,7 +38,7 @@ public:
int execute (std::string&);
private:
std::string urgencyTerm (const std::string&, float, float) const;
void urgencyTerm (ViewText&, const std::string&, float, float) const;
};
#endif

View File

@@ -104,6 +104,9 @@ int CmdProjects::execute (std::string& output)
view.add (Column::factory ("string", STRING_COLUMN_LABEL_PROJECT));
view.add (Column::factory ("string.right", STRING_COLUMN_LABEL_TASKS));
Color label (context.config.get ("color.label"));
view.colorHeader (label);
std::vector <std::string> processed;
std::map <std::string, int>::iterator project;
for (project = unique.begin (); project != unique.end (); ++project)

View File

@@ -85,6 +85,9 @@ int CmdReports::execute (std::string& output)
view.add (Column::factory ("string", STRING_CMD_REPORTS_REPORT));
view.add (Column::factory ("string", STRING_CMD_REPORTS_DESC));
Color label (context.config.get ("color.label"));
view.colorHeader (label);
// If an alternating row color is specified, notify the table.
if (context.color ())
{

View File

@@ -134,6 +134,7 @@ int CmdShow::execute (std::string& output)
" dateformat.holiday"
" dateformat.info"
" dateformat.report"
" date.iso"
" debug"
" debug.hooks"
" debug.parser"
@@ -171,6 +172,7 @@ int CmdShow::execute (std::string& output)
" monthsperline"
" nag"
" print.empty.columns"
" recurrence"
" recurrence.confirmation"
" recurrence.indicator"
" recurrence.limit"
@@ -260,6 +262,9 @@ int CmdShow::execute (std::string& output)
Color error (context.config.get ("color.error"));
Color warning (context.config.get ("color.warning"));
Color label (context.config.get ("color.label"));
view.colorHeader (label);
bool issue_error = false;
bool issue_warning = false;

View File

@@ -160,6 +160,9 @@ int CmdStats::execute (std::string& output)
view.add (Column::factory ("string", STRING_CMD_STATS_CATEGORY));
view.add (Column::factory ("string", STRING_CMD_STATS_DATA));
Color label (context.config.get ("color.label"));
view.colorHeader (label);
int row = view.addRow ();
view.set (row, 0, STRING_COLUMN_LABEL_STAT_PE);
view.set (row, 1, pendingT);

View File

@@ -127,7 +127,7 @@ int CmdSummary::execute (std::string& output)
Color bar_color (context.config.get ("color.summary.bar"));
Color bg_color (context.config.get ("color.summary.background"));
Color label (context.config.get ("color.label"));
Color label (context.config.get ("color.label"));
view.colorHeader (label);

View File

@@ -96,6 +96,9 @@ int CmdTags::execute (std::string& output)
view.add (Column::factory ("string", STRING_COLUMN_LABEL_TAG));
view.add (Column::factory ("string.right", STRING_COLUMN_LABEL_COUNT));
Color label (context.config.get ("color.label"));
view.colorHeader (label);
Color bold ("bold");
bool special = false;
std::map <std::string, int>::iterator i;

View File

@@ -99,6 +99,9 @@ int CmdTimesheet::execute (std::string& output)
completed.add (Column::factory ("string.right", STRING_COLUMN_LABEL_DUE));
completed.add (Column::factory ("string", STRING_COLUMN_LABEL_DESC));
Color label (context.config.get ("color.label"));
completed.colorHeader (label);
std::vector <Task>::iterator task;
for (task = all.begin (); task != all.end (); ++task)
{
@@ -155,6 +158,7 @@ int CmdTimesheet::execute (std::string& output)
started.add (Column::factory ("string", STRING_COLUMN_LABEL_PROJECT));
started.add (Column::factory ("string.right", STRING_COLUMN_LABEL_DUE));
started.add (Column::factory ("string", STRING_COLUMN_LABEL_DESC));
started.colorHeader (label);
for (task = all.begin (); task != all.end (); ++task)
{

View File

@@ -87,6 +87,9 @@ int CmdUDAs::execute (std::string& output)
view.add (Column::factory ("string", STRING_COLUMN_LABEL_DEFAULT));
view.add (Column::factory ("string", STRING_COLUMN_LABEL_UDACOUNT));
Color label (context.config.get ("color.label"));
view.colorHeader (label);
std::vector <std::string>::iterator uda;
for (uda = udas.begin (); uda != udas.end (); ++uda)
{
@@ -147,6 +150,9 @@ int CmdUDAs::execute (std::string& output)
orphanView.add (Column::factory ("string", STRING_COLUMN_LABEL_ORPHAN));
orphanView.add (Column::factory ("string", STRING_COLUMN_LABEL_UDACOUNT));
Color label (context.config.get ("color.label"));
orphanView.colorHeader (label);
std::map <std::string, int>::iterator o;
for (o = orphans.begin (); o != orphans.end (); ++o)
{

View File

@@ -163,29 +163,27 @@ void Command::factory (std::map <std::string, Command*>& all)
// Instantiate a command object for each custom report.
std::vector <std::string> reports;
Config::const_iterator i;
for (i = context.config.begin (); i != context.config.end (); ++i)
for (auto &i : context.config)
{
if (i->first.substr (0, 7) == "report.")
if (i.first.substr (0, 7) == "report.")
{
std::string report = i->first.substr (7);
std::string report = i.first.substr (7);
std::string::size_type columns = report.find (".columns");
if (columns != std::string::npos)
reports.push_back (report.substr (0, columns));
}
}
std::vector <std::string>::iterator report;
for (report = reports.begin (); report != reports.end (); ++report)
for (auto &report : reports)
{
// Make sure a custom report does not clash with a built-in command.
if (all.find (*report) != all.end ())
throw format (STRING_CMD_CONFLICT, *report);
if (all.find (report) != all.end ())
throw format (STRING_CMD_CONFLICT, report);
c = new CmdCustom (
*report,
"task <filter> " + *report,
context.config.get ("report." + *report + ".description"));
report,
"task <filter> " + report,
context.config.get ("report." + report + ".description"));
all[c->keyword ()] = c;
}

View File

@@ -27,6 +27,7 @@
#include <cmake.h>
#include <sstream>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <Context.h>
#include <main.h>
@@ -53,7 +54,7 @@ int Context::getWidth ()
terminal_height == 0)
{
unsigned short buff[4];
if (ioctl (fileno(stdout), TIOCGWINSZ, &buff) != -1)
if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &buff) != -1)
{
terminal_height = buff[0];
terminal_width = buff[1];
@@ -88,7 +89,7 @@ int Context::getHeight ()
terminal_height == 0)
{
unsigned short buff[4];
if (ioctl (fileno(stdout), TIOCGWINSZ, &buff) != -1)
if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &buff) != -1)
{
terminal_height = buff[0];
terminal_width = buff[1];

1016
src/l10n/deu-DEU.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -489,6 +489,8 @@
#define STRING_CMD_SYNC_HANDSHAKE "Handshake failed. {1}"
#define STRING_CMD_SYNC_TRUST_CA "You should either provide a CA certificate or override verification, but not both."
#define STRING_CMD_SYNC_TRUST_OBS "The 'taskd.trust' settings may now only contain a value of 'strict', 'ignore hostname' or 'allow all'."
// STRING_CMD_DIAG_* strings all appear on the 'diag' command output.
#define STRING_CMD_DIAG_USAGE "Platform, build and environment details"
#define STRING_CMD_DIAG_PLATFORM "Platform"
#define STRING_CMD_DIAG_UNKNOWN "<unknown>"
@@ -510,6 +512,13 @@
#define STRING_CMD_DIAG_UUID_NO_DUP "No duplicates found"
#define STRING_CMD_DIAG_NONE "-none-"
#define STRING_CMD_DIAG_HOOKS "Hooks"
#define STRING_CMD_DIAG_HOOK_NAME "unrecognized hook name"
#define STRING_CMD_DIAG_HOOK_SYMLINK "symlink"
#define STRING_CMD_DIAG_HOOK_EXEC "executable"
#define STRING_CMD_DIAG_HOOK_NO_EXEC "not executable"
#define STRING_CMD_DIAG_HOOK_ENABLE "Enabled"
#define STRING_CMD_DIAG_HOOK_DISABLE "Disabled"
#define STRING_CMD_HCOMMANDS_USAGE "Generates a list of all commands, for autocompletion purposes"
#define STRING_CMD_ZSHCOMMANDS_USAGE "Generates a list of all commands, for zsh autocompletion purposes"
#define STRING_CMD_ZSHATTS_USAGE "Generates a list of all attributes, for zsh autocompletion purposes"
@@ -788,6 +797,18 @@
#define STRING_HELPER_PROJECT_REM "({1} of {2} tasks remaining)."
#define STRING_HELPER_PROJECT_REM1 "({1} task remaining)."
// Hooks
#define STRING_HOOK_ERROR_OBJECT "Hook Error: JSON Object '{...}' expected."
#define STRING_HOOK_ERROR_NODESC "Hook Error: JSON Object missing 'description' attribute."
#define STRING_HOOK_ERROR_NOUUID "Hook Error: JSON Object missing 'uuid' attribute."
#define STRING_HOOK_ERROR_SYNTAX "Hook Error: JSON syntax error in: {1}"
#define STRING_HOOK_ERROR_JSON "Hook Error: JSON "
#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: "
#define STRING_HOOK_ERROR_BAD_NUM "Hook Error: Expected {1} JSON task(s), found {2}"
#define STRING_HOOK_ERROR_SAME1 "Hook Error: JSON must be for the same task: {1}"
#define STRING_HOOK_ERROR_SAME2 "Hook Error: JSON must be for the same task: {1} != {2}"
#define STRING_HOOK_ERROR_NOFEEDBACK "Hook Error: Expected feedback from a failing hook script."
// JSON
#define STRING_JSON_MISSING_VALUE "Error: missing value after ',' at position {1}"
#define STRING_JSON_MISSING_VALUE2 "Error: missing value at position {1}"
@@ -970,7 +991,7 @@
/*
To be included later, before the 'precendence' line.
" + - Addition, subtraction\n" \
" ! Inversion\n" \
" ~ !~ Match, no match\n" \
@@ -990,4 +1011,3 @@
#define STRING_UTIL_BYTES "B"
#endif

View File

@@ -489,64 +489,73 @@
#define STRING_CMD_SYNC_HANDSHAKE "Handshake failed. {1}"
#define STRING_CMD_SYNC_TRUST_CA "You should either provide a CA certificate or override verification, but not both."
#define STRING_CMD_SYNC_TRUST_OBS "The 'taskd.trust' settings may now only contain a value of 'strict', 'ignore hostname' or 'allow all'."
#define STRING_CMD_DIAG_USAGE "Operaciuma, kompila, kaj media detaloj"
#define STRING_CMD_DIAG_PLATFORM "Operaciumo"
#define STRING_CMD_DIAG_UNKNOWN "<nekonata>"
#define STRING_CMD_DIAG_COMPILER "Kompililo"
#define STRING_CMD_DIAG_VERSION "Versio"
#define STRING_CMD_DIAG_CAPS "Eblecoj"
// STRING_CMD_DIAG_* strings all appear on the 'diag' command output.
#define STRING_CMD_DIAG_USAGE "Platform, build and environment details"
#define STRING_CMD_DIAG_PLATFORM "Platform"
#define STRING_CMD_DIAG_UNKNOWN "<unknown>"
#define STRING_CMD_DIAG_COMPILER "Compiler"
#define STRING_CMD_DIAG_VERSION "Version"
#define STRING_CMD_DIAG_CAPS "Caps"
#define STRING_CMD_DIAG_COMPLIANCE "Compliance"
#define STRING_CMD_DIAG_FEATURES "Trajtoj de la Kompilo"
#define STRING_CMD_DIAG_BUILT "Kompilita"
#define STRING_CMD_DIAG_COMMIT "Ŝanĝaro"
#define STRING_CMD_DIAG_FOUND "(trovita)"
#define STRING_CMD_DIAG_MISSING "(mankanta)"
#define STRING_CMD_DIAG_ENABLED "Ebligita"
#define STRING_CMD_DIAG_DISABLED "Malebligita"
#define STRING_CMD_DIAG_CONFIG "Agordo"
#define STRING_CMD_DIAG_TESTS "Testoj"
#define STRING_CMD_DIAG_UUID_SCAN "Skanis {1} taskojn por trovi identajn UUID-identigilojn:"
#define STRING_CMD_DIAG_UUID_DUP "Trovis ĝemelan taskon {1}"
#define STRING_CMD_DIAG_UUID_NO_DUP "Ne trovis nenian ĝemelan taskon"
#define STRING_CMD_DIAG_NONE "-nenio-"
#define STRING_CMD_DIAG_HOOKS "Hokoj"
#define STRING_CMD_HCOMMANDS_USAGE "Produktas liston de ĉia komando, por motivo memkompletada"
#define STRING_CMD_ZSHCOMMANDS_USAGE "Produktas liston de ĉia komando, por motivo memkompletada en zsh"
#define STRING_CMD_ZSHATTS_USAGE "Produktas liston de ĉia komando, por motivo memkompletada en zsh"
#define STRING_CMD_ALIASES_USAGE "Produktas liston de ĉia alinomo, por motivo memkompletada"
#define STRING_CMD_INSTALL_USAGE "Instalas kromprogramojn kaj eksternajn skribojn"
#define STRING_CMD_DIAG_FEATURES "Build Features"
#define STRING_CMD_DIAG_BUILT "Built"
#define STRING_CMD_DIAG_COMMIT "Commit"
#define STRING_CMD_DIAG_FOUND "(found)"
#define STRING_CMD_DIAG_MISSING "(missing)"
#define STRING_CMD_DIAG_ENABLED "Enabled"
#define STRING_CMD_DIAG_DISABLED "Disabled"
#define STRING_CMD_DIAG_CONFIG "Configuration"
#define STRING_CMD_DIAG_TESTS "Tests"
#define STRING_CMD_DIAG_UUID_SCAN "Scanned {1} tasks for duplicate UUIDs:"
#define STRING_CMD_DIAG_UUID_DUP "Found duplicate {1}"
#define STRING_CMD_DIAG_UUID_NO_DUP "No duplicates found"
#define STRING_CMD_DIAG_NONE "-none-"
#define STRING_CMD_DIAG_HOOKS "Hooks"
#define STRING_CMD_DIAG_HOOK_NAME "unrecognized hook name"
#define STRING_CMD_DIAG_HOOK_SYMLINK "symlink"
#define STRING_CMD_DIAG_HOOK_EXEC "executable"
#define STRING_CMD_DIAG_HOOK_NO_EXEC "not executable"
#define STRING_CMD_DIAG_HOOK_ENABLE "Enabled"
#define STRING_CMD_DIAG_HOOK_DISABLE "Disabled"
#define STRING_CMD_MODIFY_USAGE1 "Modifas ekzistantan taskon kun argumentoj provizataj."
#define STRING_CMD_MODIFY_NO_DUE "Oni ne povas specifi reokazantan taskon sen datlimo."
#define STRING_CMD_MODIFY_REM_DUE "Oni ne povas viŝi la datlimon de reokazanta tasko."
#define STRING_CMD_MODIFY_REC_ALWAYS "Oni ne povas viŝi la reokazon de reokazanta tasko."
#define STRING_CMD_MODIFY_TASK "Modifanta taskon {1} '{2}'."
#define STRING_CMD_MODIFY_TASK_R "Modifanta reokazantan taskon {1} '{2}'."
#define STRING_CMD_MODIFY_1 "Modifis {1} taskon."
#define STRING_CMD_MODIFY_N "Modifis {1} taskojn."
#define STRING_CMD_MODIFY_NO "Ne modifis taskon."
#define STRING_CMD_MODIFY_CONFIRM "Modifi taskon {1} '{2}'?"
#define STRING_CMD_MODIFY_RECUR "Tio estas reokazanta tasko. Ĉu vi volas modifi ĉian pendantan okazon?"
#define STRING_CMD_MODIFY_NEED_TEXT "Oni devas provizi plu de teksto."
#define STRING_CMD_HCOMMANDS_USAGE "Generates a list of all commands, for autocompletion purposes"
#define STRING_CMD_ZSHCOMMANDS_USAGE "Generates a list of all commands, for zsh autocompletion purposes"
#define STRING_CMD_ZSHATTS_USAGE "Generates a list of all attributes, for zsh autocompletion purposes"
#define STRING_CMD_ALIASES_USAGE "Generates a list of all aliases, for autocompletion purposes"
#define STRING_CMD_INSTALL_USAGE "Installs extensions and external scripts"
#define STRING_CMD_COLOR_USAGE "Ĉia koloro, ekzemplero, aŭ gamo"
#define STRING_CMD_COLOR_HERE "Jen la koloroj ke estas aktuale uzataj:"
#define STRING_CMD_COLOR_COLOR "Koloro"
#define STRING_CMD_COLOR_DEFINITION "Difino"
#define STRING_CMD_COLOR_EXPLANATION "Uzu tiun komandon por vidi, kiel koloroj aspektas ĉe via terminalo."
#define STRING_CMD_COLOR_16 "Uzo 16-koloraj (subtenas substrekon, grasan tekston, brilan fonon):"
#define STRING_CMD_COLOR_256 "Uzo 256-koloraj (subtenas substrekon):"
#define STRING_CMD_COLOR_YOURS "Via ekzemplero:"
#define STRING_CMD_COLOR_BASIC "Principaj koloroj"
#define STRING_CMD_COLOR_EFFECTS "Efektoj"
#define STRING_CMD_COLOR_CUBE "Kolorkubo rgb"
#define STRING_CMD_COLOR_RAMP "Grizramplo"
#define STRING_CMD_COLOR_TRY "Provu lanĉi '{1}'."
#define STRING_CMD_COLOR_OFF "Koloroj estas aktuale malebligita ĉe via dosiero .taskrc. Por ebligi kolorojn, viŝi la vicon 'color=off', aŭ ŝanĝu 'off' al 'on'."
#define STRING_CMD_CONFIG_USAGE "Ŝanĝas agordoj de task"
#define STRING_CMD_CONFIG_CONFIRM "Ĉu vi estas certa, ke vi volas ŝanĝi la valoron de '{1}', de '{2}' al '{3}'?"
#define STRING_CMD_CONFIG_CONFIRM2 "Ĉu vi estas certa, ke vi volas aldoni '{1}' kun valoro '{2}'?"
#define STRING_CMD_CONFIG_CONFIRM3 "Ĉu vi estas certa, ke vi volas viŝi '{1}'?"
#define STRING_CMD_MODIFY_USAGE1 "Modifies the existing task with provided arguments."
#define STRING_CMD_MODIFY_NO_DUE "You cannot specify a recurring task without a due date."
#define STRING_CMD_MODIFY_REM_DUE "You cannot remove the due date from a recurring task."
#define STRING_CMD_MODIFY_REC_ALWAYS "You cannot remove the recurrence from a recurring task."
#define STRING_CMD_MODIFY_TASK "Modifying task {1} '{2}'."
#define STRING_CMD_MODIFY_TASK_R "Modifying recurring task {1} '{2}'."
#define STRING_CMD_MODIFY_1 "Modified {1} task."
#define STRING_CMD_MODIFY_N "Modified {1} tasks."
#define STRING_CMD_MODIFY_NO "Task not modified."
#define STRING_CMD_MODIFY_CONFIRM "Modify task {1} '{2}'?"
#define STRING_CMD_MODIFY_RECUR "This is a recurring task. Do you want to modify all pending recurrences of this same task?"
#define STRING_CMD_MODIFY_NEED_TEXT "Additional text must be provided."
#define STRING_CMD_COLOR_USAGE "All colors, a sample, or a legend"
#define STRING_CMD_COLOR_HERE "Here are the colors currently in use:"
#define STRING_CMD_COLOR_COLOR "Color"
#define STRING_CMD_COLOR_DEFINITION "Definition"
#define STRING_CMD_COLOR_EXPLANATION "Use this command to see how colors are displayed by your terminal."
#define STRING_CMD_COLOR_16 "16-color usage (supports underline, bold text, bright background):"
#define STRING_CMD_COLOR_256 "256-color usage (supports underline):"
#define STRING_CMD_COLOR_YOURS "Your sample:"
#define STRING_CMD_COLOR_BASIC "Basic colors"
#define STRING_CMD_COLOR_EFFECTS "Effects"
#define STRING_CMD_COLOR_CUBE "Color cube rgb"
#define STRING_CMD_COLOR_RAMP "Gray ramp"
#define STRING_CMD_COLOR_TRY "Try running '{1}'."
#define STRING_CMD_COLOR_OFF "Color is currently turned off in your .taskrc file. To enable color, remove the line 'color=off', or change the 'off' to 'on'."
#define STRING_CMD_CONFIG_USAGE "Change settings in the task configuration"
#define STRING_CMD_CONFIG_CONFIRM "Are you sure you want to change the value of '{1}' from '{2}' to '{3}'?"
#define STRING_CMD_CONFIG_CONFIRM2 "Are you sure you want to add '{1}' with a value of '{2}'?"
#define STRING_CMD_CONFIG_CONFIRM3 "Are you sure you want to remove '{1}'?"
#define STRING_CMD_CONFIG_NO_ENTRY "No entry named '{1}' found."
#define STRING_CMD_CONFIG_FILE_MOD "Agorda dosiero {1} modifita."
#define STRING_CMD_CONFIG_NO_CHANGE "Ne ŝanĝis nenion."
@@ -783,10 +792,22 @@
#define STRING_FILE_PERMS "Taskwarrior ne havas la bezonatan permeson por '{1}'."
// helpers
#define STRING_HELPER_PROJECT_CHANGE "Projekto '{1}' ŝanĝis."
#define STRING_HELPER_PROJECT_COMPL "Projekto '{1}' estas {2}% kompleta"
#define STRING_HELPER_PROJECT_REM "(Restas {1} de {2} taskoj)."
#define STRING_HELPER_PROJECT_REM1 "(Restas {1} tasko)."
#define STRING_HELPER_PROJECT_CHANGE "The project '{1}' has changed."
#define STRING_HELPER_PROJECT_COMPL "Project '{1}' is {2}% complete"
#define STRING_HELPER_PROJECT_REM "({1} of {2} tasks remaining)."
#define STRING_HELPER_PROJECT_REM1 "({1} task remaining)."
// Hooks
#define STRING_HOOK_ERROR_OBJECT "Hook Error: JSON Object '{...}' expected."
#define STRING_HOOK_ERROR_NODESC "Hook Error: JSON Object missing 'description' attribute."
#define STRING_HOOK_ERROR_NOUUID "Hook Error: JSON Object missing 'uuid' attribute."
#define STRING_HOOK_ERROR_SYNTAX "Hook Error: JSON syntax error in: {1}"
#define STRING_HOOK_ERROR_JSON "Hook Error: JSON "
#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: "
#define STRING_HOOK_ERROR_BAD_NUM "Hook Error: Expected {1} JSON task(s), found {2}"
#define STRING_HOOK_ERROR_SAME1 "Hook Error: JSON must be for the same task: {1}"
#define STRING_HOOK_ERROR_SAME2 "Hook Error: JSON must be for the same task: {1} != {2}"
#define STRING_HOOK_ERROR_NOFEEDBACK "Hook Error: Expected feedback from a failing hook script."
// JSON
#define STRING_JSON_MISSING_VALUE "Eraro: mankas valoro post ',' ĉe pozicio {1}"
@@ -970,7 +991,7 @@
/*
To be included later, before the 'precendence' line.
" + - Addition, subtraction\n" \
" ! Inversion\n" \
" ~ !~ Match, no match\n" \
@@ -990,4 +1011,3 @@
#define STRING_UTIL_BYTES "B"
#endif

View File

@@ -58,7 +58,7 @@
// 1. Copy this file (eng-USA.h) to a new file with the target locale as the
// file name. Using German as an example, do this:
//
// cp eng-USA.h de-DE.h
// cp eng-USA.h deu-DEU.h
//
// 2. Modify all the strings below.
// i.e. change "Unknown error." to "Unbekannter Fehler.".
@@ -67,7 +67,7 @@
// by inserting:
//
// #elif PACKAGE_LANGUAGE == LANGUAGE_DEU_DEU
// #include <de-DE.h>
// #include <deu-DEU.h>
//
// 4. Add your new language to task.git/CMakeLists.txt, making sure that
// number is unique:
@@ -471,6 +471,7 @@
#define STRING_TASK_NO_DESC "La anotación carece de descripción: {1}"
#define STRING_TASK_NO_ENTRY "La anotación carece de fecha de entrada: {1}"
// STRING_CMD_DIAG_* strings all appear on the 'diag' command output.
#define STRING_CMD_SYNC_USAGE "Sincroniza datos con el Servidor Task"
#define STRING_CMD_SYNC_NO_SERVER "El Servidor Task no está configurado."
#define STRING_CMD_SYNC_BAD_CRED "Credenciales del Servidor Task incorrectas."
@@ -521,6 +522,11 @@
#define STRING_CMD_DIAG_HOOKS "Hooks" // |esp-ESP|==|eng-USA|
#define STRING_CMD_HCOMMANDS_USAGE "Genera una lista de todos los comandos, con fines de auto-completado"
#define STRING_CMD_ZSHCOMMANDS_USAGE "Genera una lista de todos los comandos, con fines de auto-completado zsh"
#define STRING_CMD_DIAG_HOOK_EXEC "executable"
#define STRING_CMD_DIAG_HOOK_NO_EXEC "not executable"
#define STRING_CMD_DIAG_HOOK_ENABLE "Enabled"
#define STRING_CMD_DIAG_HOOK_DISABLE "Disabled"
#define STRING_CMD_ZSHATTS_USAGE "Generates a list of all attributes, for zsh autocompletion purposes"
#define STRING_CMD_ALIASES_USAGE "Genera una lista de todos los alias, con fines de auto-completado"
#define STRING_CMD_INSTALL_USAGE "Instala extensiones y scripts externos"
@@ -795,10 +801,22 @@
#define STRING_FILE_PERMS "Taskwarrior no tiene los permisos adecuados para '{1}'."
// helpers
#define STRING_HELPER_PROJECT_CHANGE "El proyecto '{1}' ha cambiado."
#define STRING_HELPER_PROJECT_COMPL "El proyecto '{1}' se ha completado en un {2}%"
#define STRING_HELPER_PROJECT_REM "(quedan {1} de {2} tareas)."
#define STRING_HELPER_PROJECT_REM1 "(quada {1} tasko)."
#define STRING_HELPER_PROJECT_CHANGE "The project '{1}' has changed."
#define STRING_HELPER_PROJECT_COMPL "Project '{1}' is {2}% complete"
#define STRING_HELPER_PROJECT_REM "({1} of {2} tasks remaining)."
#define STRING_HELPER_PROJECT_REM1 "({1} task remaining)."
// Hooks
#define STRING_HOOK_ERROR_OBJECT "Hook Error: JSON Object '{...}' expected."
#define STRING_HOOK_ERROR_NODESC "Hook Error: JSON Object missing 'description' attribute."
#define STRING_HOOK_ERROR_NOUUID "Hook Error: JSON Object missing 'uuid' attribute."
#define STRING_HOOK_ERROR_SYNTAX "Hook Error: JSON syntax error in: {1}"
#define STRING_HOOK_ERROR_JSON "Hook Error: JSON "
#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: "
#define STRING_HOOK_ERROR_BAD_NUM "Hook Error: Expected {1} JSON task(s), found {2}"
#define STRING_HOOK_ERROR_SAME1 "Hook Error: JSON must be for the same task: {1}"
#define STRING_HOOK_ERROR_SAME2 "Hook Error: JSON must be for the same task: {1} != {2}"
#define STRING_HOOK_ERROR_NOFEEDBACK "Hook Error: Expected feedback from a failing hook script."
// JSON
#define STRING_JSON_MISSING_VALUE "Error: falta valor después de ',' en posición {1}"
@@ -1018,4 +1036,3 @@
#define STRING_UTIL_BYTES "B" // |esp-ESP|==|eng-USA|
#endif

View File

@@ -58,7 +58,7 @@
// 1. Copy this file (eng-USA.h) to a new file with the target locale as the
// file name. Using German as an example, do this:
//
// cp eng-USA.h de-DE.h
// cp eng-USA.h deu-DEU.h
//
// 2. Modify all the strings below.
// i.e. change "Unknown error." to "Unbekannter Fehler.".
@@ -67,7 +67,7 @@
// by inserting:
//
// #elif PACKAGE_LANGUAGE == LANGUAGE_DEU_DEU
// #include <de-DE.h>
// #include <deu-DEU.h>
//
// 4. Add your new language to task.git/CMakeLists.txt, making sure that
// number is unique:
@@ -261,6 +261,7 @@
#define STRING_COLUMN_EXAMPLES_ANNO4 "If you're not getting your hair cut"
// commands/Cmd*
// USAGE strings are visible in 'task help'
#define STRING_CMD_CONFLICT "Le rapport personnalisé '{1}' entre en conflit avec une commande prédéfinie de task."
#define STRING_CMD_VERSION_USAGE "Affiche le numéro de version de taskwarrior"
#define STRING_CMD_VERSION_USAGE2 "Affiche uniquement le numéro de version de taskwarrior"
@@ -488,6 +489,8 @@
#define STRING_CMD_SYNC_HANDSHAKE "Handshake failed. {1}"
#define STRING_CMD_SYNC_TRUST_CA "You should either provide a CA certificate or override verification, but not both."
#define STRING_CMD_SYNC_TRUST_OBS "The 'taskd.trust' settings may now only contain a value of 'strict', 'ignore hostname' or 'allow all'."
// STRING_CMD_DIAG_* strings all appear on the 'diag' command output.
#define STRING_CMD_DIAG_USAGE "Platform, build and environment details"
#define STRING_CMD_DIAG_PLATFORM "Platform"
#define STRING_CMD_DIAG_UNKNOWN "<unknown>"
@@ -509,6 +512,13 @@
#define STRING_CMD_DIAG_UUID_NO_DUP "No duplicates found"
#define STRING_CMD_DIAG_NONE "-none-"
#define STRING_CMD_DIAG_HOOKS "Hooks"
#define STRING_CMD_DIAG_HOOK_NAME "unrecognized hook name"
#define STRING_CMD_DIAG_HOOK_SYMLINK "symlink"
#define STRING_CMD_DIAG_HOOK_EXEC "executable"
#define STRING_CMD_DIAG_HOOK_NO_EXEC "not executable"
#define STRING_CMD_DIAG_HOOK_ENABLE "Enabled"
#define STRING_CMD_DIAG_HOOK_DISABLE "Disabled"
#define STRING_CMD_HCOMMANDS_USAGE "Generates a list of all commands, for autocompletion purposes"
#define STRING_CMD_ZSHCOMMANDS_USAGE "Generates a list of all commands, for zsh autocompletion purposes"
#define STRING_CMD_ZSHATTS_USAGE "Generates a list of all attributes, for zsh autocompletion purposes"
@@ -787,6 +797,18 @@
#define STRING_HELPER_PROJECT_REM "({1} of {2} tasks remaining)."
#define STRING_HELPER_PROJECT_REM1 "({1} task remaining)."
// Hooks
#define STRING_HOOK_ERROR_OBJECT "Hook Error: JSON Object '{...}' expected."
#define STRING_HOOK_ERROR_NODESC "Hook Error: JSON Object missing 'description' attribute."
#define STRING_HOOK_ERROR_NOUUID "Hook Error: JSON Object missing 'uuid' attribute."
#define STRING_HOOK_ERROR_SYNTAX "Hook Error: JSON syntax error in: {1}"
#define STRING_HOOK_ERROR_JSON "Hook Error: JSON "
#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: "
#define STRING_HOOK_ERROR_BAD_NUM "Hook Error: Expected {1} JSON task(s), found {2}"
#define STRING_HOOK_ERROR_SAME1 "Hook Error: JSON must be for the same task: {1}"
#define STRING_HOOK_ERROR_SAME2 "Hook Error: JSON must be for the same task: {1} != {2}"
#define STRING_HOOK_ERROR_NOFEEDBACK "Hook Error: Expected feedback from a failing hook script."
// JSON
#define STRING_JSON_MISSING_VALUE "Erreur : valeur manquante après ',' à la position {1}"
#define STRING_JSON_MISSING_VALUE2 "Erreur : valeur manquante à la position {1}"
@@ -989,4 +1011,3 @@
#define STRING_UTIL_BYTES "o"
#endif

View File

@@ -58,7 +58,7 @@
// 1. Copy this file (eng-USA.h) to a new file with the target locale as the
// file name. Using German as an example, do this:
//
// cp eng-USA.h de-DE.h
// cp eng-USA.h deu-DEU.h
//
// 2. Modify all the strings below.
// i.e. change "Unknown error." to "Unbekannter Fehler.".
@@ -67,7 +67,7 @@
// by inserting:
//
// #elif PACKAGE_LANGUAGE == LANGUAGE_DEU_DEU
// #include <de-DE.h>
// #include <deu-DEU.h>
//
// 4. Add your new language to task.git/CMakeLists.txt, making sure that
// number is unique:
@@ -91,7 +91,6 @@
#ifndef INCLUDED_STRINGS
#define INCLUDED_STRINGS
#define L10N // Localization complete.
// Note that for English, the following two lines are not displayed. For all
// other localizations, these lines will appear in the output of the 'version'
@@ -141,9 +140,9 @@
#define STRING_VARIANT_MUL_STR_DUR "Cannot multiply strings by durations"
#define STRING_VARIANT_MUL_DUR_STR "Cannot multiply durations by strings"
#define STRING_VARIANT_MUL_DUR_DATE "Cannot multiply durations by dates"
#define STRING_VARIANT_MUL_DUR_DUR "Cannot multiply durations by duraitons"
#define STRING_VARIANT_MUL_DUR_DUR "Cannot multiply durations by durations"
#define STRING_VARIANT_DIV_UNKNOWN "Cannot divide unknown type"
#define STRING_VARIANT_DIV_BOOL "Cannot divide Boolean"
#define STRING_VARIANT_DIV_BOOL "Cannot divide Boolean values"
#define STRING_VARIANT_DIV_INT_BOOL "Cannot divide integers by Boolean values"
#define STRING_VARIANT_DIV_ZERO "Cannot divide by zero"
#define STRING_VARIANT_DIV_INT_STR "Cannot divide integer by string"
@@ -155,7 +154,7 @@
#define STRING_VARIANT_DIV_DUR_STR "Cannot divide durations by strings"
#define STRING_VARIANT_DIV_DUR_DATE "Cannot divide durations by dates"
#define STRING_VARIANT_DIV_DUR_DUR "Cannot divide durations by durations"
#define STRING_VARIANT_MOD_BOOL "Cannot modulo Boolean"
#define STRING_VARIANT_MOD_BOOL "Cannot modulo Booleans"
#define STRING_VARIANT_MOD_DATE "Cannot modulo date values"
#define STRING_VARIANT_MOD_DUR "Cannot modulo duration values"
#define STRING_VARIANT_MOD_INT_BOOL "Cannot modulo integer by Boolean"
@@ -178,13 +177,13 @@
// These are errors generated at the lowest level of input analysis,
// at the character level.
#define STRING_LEX_IMMEDIATE_UNK "Lexer start failure, unknown characters found immediately."
#define STRING_LEX_TYPE_UNK "Lexer cannot process and unknown token type."
#define STRING_LEX_TYPE_UNK "Lexer cannot process an unknown token type."
// Dates
//
// These are errors generated when parsing date values.
#define STRING_DATES_MONTH_31 "Error: no month has more than 31 days."
#define STRING_DATES_ORD_MISMATCH "Error: number and ordinal don't match (ie '1st', not '1nd')."
#define STRING_DATES_ORD_MISMATCH "Error: number and ordinal don't match (e.g., '1st', not '1nd')."
// Color
#define STRING_COLOR_UNRECOGNIZED "Il colore '{1}' non è riconosciuto."
@@ -489,6 +488,8 @@
#define STRING_CMD_SYNC_HANDSHAKE "Handshake failed. {1}"
#define STRING_CMD_SYNC_TRUST_CA "You should either provide a CA certificate or override verification, but not both."
#define STRING_CMD_SYNC_TRUST_OBS "The 'taskd.trust' settings may now only contain a value of 'strict', 'ignore hostname' or 'allow all'."
// STRING_CMD_DIAG_* strings all appear on the 'diag' command output.
#define STRING_CMD_DIAG_USAGE "Dettagli su piattaforma, build e ambiente"
#define STRING_CMD_DIAG_PLATFORM "Piattaforma"
#define STRING_CMD_DIAG_UNKNOWN "<sconoscito>"
@@ -510,6 +511,13 @@
#define STRING_CMD_DIAG_UUID_NO_DUP "Nessun duplicato trovato"
#define STRING_CMD_DIAG_NONE "-nessuno-"
#define STRING_CMD_DIAG_HOOKS "Hooks"
#define STRING_CMD_DIAG_HOOK_NAME "unrecognized hook name"
#define STRING_CMD_DIAG_HOOK_SYMLINK "symlink"
#define STRING_CMD_DIAG_HOOK_EXEC "executable"
#define STRING_CMD_DIAG_HOOK_NO_EXEC "not executable"
#define STRING_CMD_DIAG_HOOK_ENABLE "Enabled"
#define STRING_CMD_DIAG_HOOK_DISABLE "Disabled"
#define STRING_CMD_HCOMMANDS_USAGE "Genera la lista di tutti i comandi, per autocompletamento"
#define STRING_CMD_ZSHCOMMANDS_USAGE "Genera la lista di tutti i comandi, per autocompletamento in zsh"
#define STRING_CMD_ZSHATTS_USAGE "Generates a list of all attributes, for zsh autocompletion purposes"
@@ -788,6 +796,18 @@
#define STRING_HELPER_PROJECT_REM "({1} di {2} task rimanenti)."
#define STRING_HELPER_PROJECT_REM1 "({1} task remaining)."
// Hooks
#define STRING_HOOK_ERROR_OBJECT "Hook Error: JSON Object '{...}' expected."
#define STRING_HOOK_ERROR_NODESC "Hook Error: JSON Object missing 'description' attribute."
#define STRING_HOOK_ERROR_NOUUID "Hook Error: JSON Object missing 'uuid' attribute."
#define STRING_HOOK_ERROR_SYNTAX "Hook Error: JSON syntax error in: {1}"
#define STRING_HOOK_ERROR_JSON "Hook Error: JSON "
#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: "
#define STRING_HOOK_ERROR_BAD_NUM "Hook Error: Expected {1} JSON task(s), found {2}"
#define STRING_HOOK_ERROR_SAME1 "Hook Error: JSON must be for the same task: {1}"
#define STRING_HOOK_ERROR_SAME2 "Hook Error: JSON must be for the same task: {1} != {2}"
#define STRING_HOOK_ERROR_NOFEEDBACK "Hook Error: Expected feedback from a failing hook script."
// JSON
#define STRING_JSON_MISSING_VALUE "Errore: mancato valore dopo ',' alla posizione {1}"
#define STRING_JSON_MISSING_VALUE2 "Errore: mancato valore alla posizione {1}"
@@ -990,4 +1010,3 @@
#define STRING_UTIL_BYTES "B"
#endif

View File

@@ -489,6 +489,8 @@
#define STRING_CMD_SYNC_HANDSHAKE "Nieudany handshake. {1}"
#define STRING_CMD_SYNC_TRUST_CA "Powinieneś udostępnić certyfikat CA lub nadpisać weryfikację. Wybierz jedną z opcji."
#define STRING_CMD_SYNC_TRUST_OBS "The 'taskd.trust' settings may now only contain a value of 'strict', 'ignore hostname' or 'allow all'."
// STRING_CMD_DIAG_* strings all appear on the 'diag' command output.
#define STRING_CMD_DIAG_USAGE "Szczegóły platformy i środowiska budowania"
#define STRING_CMD_DIAG_PLATFORM "Platforma"
#define STRING_CMD_DIAG_UNKNOWN "<nieznane>"
@@ -510,6 +512,13 @@
#define STRING_CMD_DIAG_UUID_NO_DUP "Nie znaleziono duplikatów"
#define STRING_CMD_DIAG_NONE "-brak-"
#define STRING_CMD_DIAG_HOOKS "Haki"
#define STRING_CMD_DIAG_HOOK_NAME "unrecognized hook name"
#define STRING_CMD_DIAG_HOOK_SYMLINK "symlink"
#define STRING_CMD_DIAG_HOOK_EXEC "executable"
#define STRING_CMD_DIAG_HOOK_NO_EXEC "not executable"
#define STRING_CMD_DIAG_HOOK_ENABLE "Enabled"
#define STRING_CMD_DIAG_HOOK_DISABLE "Disabled"
#define STRING_CMD_HCOMMANDS_USAGE "Generuje listę wszystkich poleceń dla funkcji autouzupełniania"
#define STRING_CMD_ZSHCOMMANDS_USAGE "Generuje listę wszystkich poleceń dla funkcji autouzupełniania w powłoce zsh"
#define STRING_CMD_ZSHATTS_USAGE "Generates a list of all attributes, for zsh autocompletion purposes"
@@ -783,10 +792,22 @@
#define STRING_FILE_PERMS "Taskwarrior nie posiada praw dostępu do '{1}'."
// helpers
#define STRING_HELPER_PROJECT_CHANGE "Projekt '{1}' uległ zmianie."
#define STRING_HELPER_PROJECT_COMPL "Projekt '{1}' w {2}% ukończony"
#define STRING_HELPER_PROJECT_REM "({1} z {2} zadań pozostało)."
#define STRING_HELPER_PROJECT_REM1 "({1} zadań pozostało)."
#define STRING_HELPER_PROJECT_CHANGE "The project '{1}' has changed."
#define STRING_HELPER_PROJECT_COMPL "Project '{1}' is {2}% complete"
#define STRING_HELPER_PROJECT_REM "({1} of {2} tasks remaining)."
#define STRING_HELPER_PROJECT_REM1 "({1} task remaining)."
// Hooks
#define STRING_HOOK_ERROR_OBJECT "Hook Error: JSON Object '{...}' expected."
#define STRING_HOOK_ERROR_NODESC "Hook Error: JSON Object missing 'description' attribute."
#define STRING_HOOK_ERROR_NOUUID "Hook Error: JSON Object missing 'uuid' attribute."
#define STRING_HOOK_ERROR_SYNTAX "Hook Error: JSON syntax error in: {1}"
#define STRING_HOOK_ERROR_JSON "Hook Error: JSON "
#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: "
#define STRING_HOOK_ERROR_BAD_NUM "Hook Error: Expected {1} JSON task(s), found {2}"
#define STRING_HOOK_ERROR_SAME1 "Hook Error: JSON must be for the same task: {1}"
#define STRING_HOOK_ERROR_SAME2 "Hook Error: JSON must be for the same task: {1} != {2}"
#define STRING_HOOK_ERROR_NOFEEDBACK "Hook Error: Expected feedback from a failing hook script."
// JSON
#define STRING_JSON_MISSING_VALUE "Błąd: brak wartości po ',' na pozycji {1}"

View File

@@ -58,7 +58,7 @@
// 1. Copy this file (eng-USA.h) to a new file with the target locale as the
// file name. Using German as an example, do this:
//
// cp eng-USA.h de-DE.h
// cp eng-USA.h deu-DEU.h
//
// 2. Modify all the strings below.
// i.e. change "Unknown error." to "Unbekannter Fehler.".
@@ -67,7 +67,7 @@
// by inserting:
//
// #elif PACKAGE_LANGUAGE == LANGUAGE_DEU_DEU
// #include <de-DE.h>
// #include <deu-DEU.h>
//
// 4. Add your new language to task.git/CMakeLists.txt, making sure that
// number is unique:
@@ -489,6 +489,8 @@
#define STRING_CMD_SYNC_HANDSHAKE "'Handshake' falhou. {1}"
#define STRING_CMD_SYNC_TRUST_CA "Deve fornecer um certificado CA ou desactivar a verificação, mas não ambos."
#define STRING_CMD_SYNC_TRUST_OBS "The 'taskd.trust' settings may now only contain a value of 'strict', 'ignore hostname' or 'allow all'."
// STRING_CMD_DIAG_* strings all appear on the 'diag' command output.
#define STRING_CMD_DIAG_USAGE "Plataforma, versão e detalhes de ambiente"
#define STRING_CMD_DIAG_PLATFORM "Plataforma"
#define STRING_CMD_DIAG_UNKNOWN "<desconhecido>"
@@ -510,6 +512,13 @@
#define STRING_CMD_DIAG_UUID_NO_DUP "Nenhum duplicado encontrado"
#define STRING_CMD_DIAG_NONE "-nenhum-"
#define STRING_CMD_DIAG_HOOKS "Âncoras"
#define STRING_CMD_DIAG_HOOK_NAME "unrecognized hook name"
#define STRING_CMD_DIAG_HOOK_SYMLINK "symlink"
#define STRING_CMD_DIAG_HOOK_EXEC "executable"
#define STRING_CMD_DIAG_HOOK_NO_EXEC "not executable"
#define STRING_CMD_DIAG_HOOK_ENABLE "Enabled"
#define STRING_CMD_DIAG_HOOK_DISABLE "Disabled"
#define STRING_CMD_HCOMMANDS_USAGE "Gera uma lista com todos os comandos, para fins de terminação automática"
#define STRING_CMD_ZSHCOMMANDS_USAGE "Gera uma lista com todos os comandos, para terminação automática em zsh"
#define STRING_CMD_ZSHATTS_USAGE "Gera uma lista de todos os atributos, para terminação automática em zsh"
@@ -783,10 +792,22 @@
#define STRING_FILE_PERMS "O taskwarrior não encontrou as permissões corretas em '{1}'."
// helpers
#define STRING_HELPER_PROJECT_CHANGE "O projeto '{1}' foi alterado."
#define STRING_HELPER_PROJECT_COMPL "Projeto '{1}' está {2}% concluído"
#define STRING_HELPER_PROJECT_REM "(Restam {1} de {2} tarefas)."
#define STRING_HELPER_PROJECT_REM1 "({1} tarefas em falta)."
#define STRING_HELPER_PROJECT_CHANGE "The project '{1}' has changed."
#define STRING_HELPER_PROJECT_COMPL "Project '{1}' is {2}% complete"
#define STRING_HELPER_PROJECT_REM "({1} of {2} tasks remaining)."
#define STRING_HELPER_PROJECT_REM1 "({1} task remaining)."
// Hooks
#define STRING_HOOK_ERROR_OBJECT "Hook Error: JSON Object '{...}' expected."
#define STRING_HOOK_ERROR_NODESC "Hook Error: JSON Object missing 'description' attribute."
#define STRING_HOOK_ERROR_NOUUID "Hook Error: JSON Object missing 'uuid' attribute."
#define STRING_HOOK_ERROR_SYNTAX "Hook Error: JSON syntax error in: {1}"
#define STRING_HOOK_ERROR_JSON "Hook Error: JSON "
#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: "
#define STRING_HOOK_ERROR_BAD_NUM "Hook Error: Expected {1} JSON task(s), found {2}"
#define STRING_HOOK_ERROR_SAME1 "Hook Error: JSON must be for the same task: {1}"
#define STRING_HOOK_ERROR_SAME2 "Hook Error: JSON must be for the same task: {1} != {2}"
#define STRING_HOOK_ERROR_NOFEEDBACK "Hook Error: Expected feedback from a failing hook script."
// JSON
#define STRING_JSON_MISSING_VALUE "Erro: valor em falta após ',' na posição {1}"
@@ -990,4 +1011,3 @@
#define STRING_UTIL_BYTES "B" // |por-PRT|==|eng-USA|
#endif

View File

@@ -54,6 +54,11 @@ extern Context context;
// child tasks need to be generated to fill gaps.
void handleRecurrence ()
{
// Recurrence can be disabled.
// Note: This is currently a workaround for TD-44, TW-1520.
if (! context.config.getBoolean ("recurrence"))
return;
std::vector <Task> tasks = context.tdb2.pending.get_tasks ();
Date now;

View File

@@ -985,7 +985,11 @@ std::string rightJustify (const int input, const int width)
////////////////////////////////////////////////////////////////////////////////
std::string rightJustify (const std::string& input, const int width)
{
return std::string (width - utf8_text_width (input), ' ') + input;
unsigned int len = utf8_text_width (input);
return ((width > len)
? std::string (width - len, ' ')
: "")
+ input;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -153,7 +153,7 @@ int autoComplete (
{
matches.clear ();
// Handle trivial case.
// Handle trivial case.
unsigned int length = partial.length ();
if (length)
{
@@ -276,7 +276,7 @@ int execute (
struct timeval tv;
int select_retval, read_retval, write_retval;
char buf[16384];
int written;
unsigned int written;
const char* input_cstr = input.c_str ();
if (signal (SIGPIPE, SIG_IGN) == SIG_ERR) // Handled locally with EPIPE.
@@ -294,27 +294,37 @@ int execute (
if (pid == 0)
{
// This is only reached in the child
close (pin[1]); // Close the write end of the input pipe.
close (pout[0]); // Close the read end of the output pipe.
if (dup2 (pin[0], STDIN_FILENO) == -1)
throw std::string (std::strerror (errno));
if (dup2 (pout[1], STDOUT_FILENO) == -1)
throw std::string (std::strerror (errno));
char** argv = new char* [args.size () + 1];
char** argv = new char* [args.size () + 2];
argv[0] = (char*) executable.c_str ();
for (unsigned int i = 0; i < args.size (); ++i)
argv[i] = (char*) args[i].c_str ();
argv[i+1] = (char*) args[i].c_str ();
argv[args.size ()] = NULL;
argv[args.size () + 1] = NULL;
_exit (execvp (executable.c_str (), argv));
}
// This is only reached in the parent
close (pin[0]); // Close the read end of the input pipe.
close (pout[1]); // Close the write end if the output pipe.
close (pout[1]); // Close the write end of the output pipe.
if (input.size () == 0)
{
// Nothing to send to the child, close the pipe early.
close (pin[1]);
}
read_retval = -1;
written = 0;
while (read_retval != 0 || input.size () - written != 0)
while (read_retval != 0 || input.size () != written)
{
FD_ZERO (&rfds);
if (read_retval != 0)
@@ -323,7 +333,7 @@ int execute (
}
FD_ZERO (&wfds);
if (input.size () - written != 0)
if (input.size () != written)
{
FD_SET (pin[1], &wfds);
}
@@ -353,6 +363,12 @@ int execute (
}
}
written += write_retval;
if (written == input.size ())
{
// Let the child know that no more input is coming by closing the pipe.
close (pin[1]);
}
}
if (FD_ISSET (pout[0], &rfds))
@@ -366,7 +382,6 @@ int execute (
}
}
close (pin[1]); // Close the write end of the input pipe.
close (pout[0]); // Close the read end of the output pipe.
int status = -1;
@@ -382,6 +397,9 @@ int execute (
throw std::string ("Error: Could not get Hook exit status!");
}
if (signal (SIGPIPE, SIG_DFL) == SIG_ERR) // We're done, return to default.
throw std::string (std::strerror (errno));
return status;
}

110
test/README Normal file
View File

@@ -0,0 +1,110 @@
README
======
This is the task.git/test/README file, and contains notes about the Taskwarrior
test suite.
Running Tests
-------------
TL;DR cd test && make && ./run_all && ./problems
All unit tests produce TAP (Test Anything Protocol) output, and are run by the
'run_all' test harness.
The 'run_all' script produces an 'all.log' file which is the accumulated output
of all tests. Before executing 'run_all' you need to compile the C++ unit
tests, by running 'make' on the 'test' directory.
The script 'problems' will list all the tests that fail, with a count of the
failing tests.
Any TAP harness may be used.
Architecture
------------
There are three varieties of tests:
* Perl unit tests that use Test::More and the JSON module. We are phasing
these out, and will accept no new Perl tests. These tests are high level
and exercise Taskwarrior at the command line level.
* C++ unit tests that test low-level object interfaces. These are typically
very fast tests, and are exhaustive in nature.
* Python unit tests that are at the highest level, exercising the command
line, hooks and syncing. There is an example, 'template.t', that shows how
to perform various high level tests.
All tests are named with the pattern '*.t', and any other forms are not run by
the test harness. Additionally a test must be set executable (chmod +x) for it
to be run. In the case of Perl and Python tests one can still run them manually
by launching them with 'perl/python test.t'. It also allows us to keep tests
submitted for bugs that are not scheduled to be fixed in the upcoming release,
and we don't want the failing tests to prevent us from seeing 100% pass rate
for the bugs we *have* fixed.
Goals
-----
The test suite is evolving, and becoming a better tool for determining whether
code is ready for release. There are goals that shape these changes, and they
are:
* Migrate test suite to Python and C++, eliminating all Perl. The Python
test suite is more expressive and high level. Migrating reduces
dependencies.
* Increase test coverage by testing more features, more thoroughly.
* Write fewer bug regression tests. Over time, bug regression tests are less
useful than feature tests, and more likely to contain overlapping coverage.
* The Python test suite provides test isolation, such that each test is run
in a separate directory. This will allow eventual parallelization, but not
until the Perl tests are eliminated.
* Eliminate obsolete tests, which are tests that have overlapping coverage.
This means migrate bug-specific tests to feature tests.
* Categorize the tests, restructure the directories.
What Makes a Good Test
----------------------
A good test ensures that a feature is functioning as expected, and contains
both positive and negative aspects, or in other words looks for expected
behavior as well as looking for the absence of unexpected behavior.
How to Submit a Test Change/Addition
------------------------------------
Mail it to support@taskwarrior.org, or attach it to an open bug.
TODO
----
For anyone looking for test-related tasks to take on, here are some suggestions:
* Take tw-285.t and improve it to test more (if not all) virtual tags, then
rename it virtual-tags.t.
* Select a bug.*.t Perl test and convert it to Python using the template.
* Look at the latest todo.txt file format spec, and make sure that
import.todo.sh.t is testing the current format.
* Select a feature.*.t Perl test, convert it to Python using the template,
then rename it to <feature>.t
* Find and eliminate individual test that do the same thing.
---

View File

@@ -1,131 +0,0 @@
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
import re
class TAP:
"""TAP-compliant unit test class."""
def __init__(self, planned = 0):
self.planned = planned
self.counter = 0
self.passed = 0
self.failed = 0
self.skipped = 0
print "1..%d " % self.planned
def __del__(self):
percentPassed = 0.0
if self.planned > 0:
percentPassed = (100.0 * self.passed) / max (self.planned, self.passed + self.failed + self.skipped)
if self.counter < self.planned:
print "# Only %d tests, out of a planned %d were run." % (self.counter, self.planned)
self.skipped += self.planned - self.counter
elif self.counter > self.planned:
print "# %d tests were run, but only %d were planned." % (self.counter, self.planned)
print "# %d passed, %d failed, %d skipped. %.2f%% passed." % (self.passed, self.failed, self.skipped, percentPassed)
def plan(self, planned):
self.planned = planned
print "1..%d " % self.planned
def planMore(self, extra):
self.planned += extra
print "1..%d" % self.planned
def ok(self, expression, description):
self.counter += 1
if bool(expression):
self.passed += 1
print "ok %d - %s" % (self.counter, description)
else:
self.failed += 1
print "not ok %d - %s" % (self.counter, description)
def notok(self, expression, description):
self.counter += 1
if not bool(expression):
self.passed += 1
print "ok %d - %s" % (self.counter, description)
else:
self.failed += 1
print "not ok %d - %s" % (self.counter, description)
def equals(self, actual, expected, description):
self.counter += 1
if actual == expected:
self.passed += 1
print "ok %d - %s" % (self.counter, description)
else:
self.failed += 1
print "not ok %d - %s" % (self.counter, description)
print "# expected:", expected, "\n# got:", actual
def like(self, actual, pattern, description):
self.counter += 1
if re.search(pattern, actual):
self.passed += 1
print "ok %d - %s" % (self.counter, description)
else:
self.failed += 1
print "not ok %d - %s" % (self.counter, description)
def unlike(self, actual, pattern, description):
self.counter += 1
if re.search(pattern, actual):
self.failed += 1
print "not ok %d - %s" % (self.counter, description)
else:
self.passed += 1
print "ok %d - %s" % (self.counter, description)
def diag(self, stuff):
for line in stuff.strip().split("\n"):
print "#", line.strip()
def skip(self, message):
self.counter += 1
self.skipped += 1
print "skip %d %s" % (self.counter, message)
def passed(self, message):
self.counter += 1
self.passed += 1
print "ok %d %s" % (self.counter, message)
def fail(self, message):
self.counter += 1
self.failed += 1
print "not ok %d %s" % (self.counter, message)
def skip(self, message):
self.counter += 1
self.skipped += 1
print "skip %d %s" % (self.counter, message)
################################################################################

View File

@@ -1,101 +1,94 @@
#! /usr/bin/env perl
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
###############################################################################
#
# Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# http://www.opensource.org/licenses/mit-license.php
#
###############################################################################
use strict;
use warnings;
use Test::More tests => 19;
import sys
import os
import unittest
# Ensure python finds the local simpletap module
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
# Ensure environment has no influence.
delete $ENV{'TASKDATA'};
delete $ENV{'TASKRC'};
from basetest import Task, TestCase, Taskd, ServerTestCase # noqa
use File::Basename;
my $ut = basename ($0);
my $rc = $ut . '.rc';
# Create the rc file.
if (open my $fh, '>', $rc)
{
print $fh "data.location=.\n",
"abbreviation.minimum=1\n";
close $fh;
}
class TestAbbreviation(TestCase):
def setUp(self):
self.t = Task()
self.t.config("abbreviation.minimum", "1")
# Test the priority attribute abbrevations.
qx{../src/task rc:$rc add priority:H with 2>&1};
qx{../src/task rc:$rc add without 2>&1};
self.t(("add", "project:home", "priority:H", "hasattributes"))
self.t(("add", "noattributes"))
my $output = qx{../src/task rc:$rc list priority:H 2>&1};
like ($output, qr/\bwith\b/, "$ut: priority:H with");
unlike ($output, qr/\bwithout\b/, "$ut: priority:H without");
def verify_attibute(self, expr):
code, out, err = self.t(("list", expr))
$output = qx{../src/task rc:$rc list priorit:H 2>&1};
like ($output, qr/\bwith\b/, "$ut: priorit:H with");
unlike ($output, qr/\bwithout\b/, "$ut: priorit:H without");
self.assertIn("hasattributes", out, msg=expr + " hasattributes")
self.assertNotIn("noattributes", out, msg=expr + " noattributes")
$output = qx{../src/task rc:$rc list priori:H 2>&1};
like ($output, qr/\bwith\b/, "$ut: priori:H with");
unlike ($output, qr/\bwithout\b/, "$ut: priori:H without");
def test_attribute_abbreviations(self):
"Test project attribute abbrevations"
$output = qx{../src/task rc:$rc list prior:H 2>&1};
like ($output, qr/\bwith\b/, "$ut: prior:H with");
unlike ($output, qr/\bwithout\b/, "$ut: prior:H without");
self.verify_attibute("project:home")
self.verify_attibute("projec:home")
self.verify_attibute("proje:home")
self.verify_attibute("proj:home")
self.verify_attibute("pro:home")
$output = qx{../src/task rc:$rc list prio:H 2>&1};
like ($output, qr/\bwith\b/, "$ut: prio:H with");
unlike ($output, qr/\bwithout\b/, "$ut: prio:H without");
def test_uda_abbreviations(self):
"Test uda attribute abbrevations"
# NOTE This will be an UDA when TW-1541 is closed, for now it's just
# one more attribute
$output = qx{../src/task rc:$rc list pri:H 2>&1};
like ($output, qr/\bwith\b/, "$ut: pri:H with");
unlike ($output, qr/\bwithout\b/, "$ut: pri:H without");
self.verify_attibute("priority:H")
self.verify_attibute("priorit:H")
self.verify_attibute("priori:H")
self.verify_attibute("prior:H")
self.verify_attibute("prio:H")
self.verify_attibute("pri:H")
# Test the version command abbreviations.
$output = qx{../src/task rc:$rc version 2>&1};
like ($output, qr/MIT\s+license/, "$ut: version");
def verify_command(self, cmd):
code, out, err = self.t((cmd,))
$output = qx{../src/task rc:$rc versio 2>&1};
like ($output, qr/MIT\s+license/, "$ut: versio");
self.assertIn("MIT license", out, msg=cmd)
$output = qx{../src/task rc:$rc versi 2>&1};
like ($output, qr/MIT\s+license/, "$ut: versi");
def test_command_abbreviations(self):
"Test version command abbrevations"
$output = qx{../src/task rc:$rc vers 2>&1};
like ($output, qr/MIT\s+license/, "$ut: vers");
self.verify_command("version")
self.verify_command("versio")
self.verify_command("versi")
self.verify_command("vers")
self.verify_command("ver")
self.verify_command("ve")
self.verify_command("v")
$output = qx{../src/task rc:$rc ver 2>&1};
like ($output, qr/MIT\s+license/, "$ut: ver");
$output = qx{../src/task rc:$rc ve 2>&1};
like ($output, qr/MIT\s+license/, "$ut: ve");
$output = qx{../src/task rc:$rc v 2>&1};
like ($output, qr/MIT\s+license/, "$ut: v");
# Cleanup.
unlink qw(pending.data completed.data undo.data backlog.data), $rc;
exit 0;
if __name__ == "__main__":
from simpletap import TAPTestRunner
unittest.main(testRunner=TAPTestRunner())
# vim: ai sts=4 et sw=4

View File

@@ -1,68 +1,72 @@
#! /usr/bin/env perl
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
###############################################################################
#
# Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# http://www.opensource.org/licenses/mit-license.php
#
###############################################################################
use strict;
use warnings;
use Test::More tests => 7;
import sys
import os
import unittest
# Ensure python finds the local simpletap module
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
# Ensure environment has no influence.
delete $ENV{'TASKDATA'};
delete $ENV{'TASKRC'};
from basetest import Task, TestCase
from basetest.utils import UUID_regex
use File::Basename;
my $ut = basename ($0);
my $rc = $ut . '.rc';
# Create the rc file.
if (open my $fh, '>', $rc)
{
print $fh "data.location=.\n",
"confirmation=off\n";
close $fh;
}
class TestAdd(TestCase):
def setUp(self):
self.t = Task()
self.t(("add", "This is a test"))
# Test the add command.
qx{../src/task rc:$rc add This is a test 2>&1};
my $output = qx{../src/task rc:$rc info 1 2>&1};
like ($output, qr/ID\s+1\n/, "$ut: add ID");
like ($output, qr/Description\s+This is a test\n/, "$ut: add ID");
like ($output, qr/Status\s+Pending\n/, "$ut: add Pending");
like ($output, qr/UUID\s+[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\n/,
"$ut: add UUID");
def test_add(self):
"Testing add command"
# Test the /// modifier.
qx{../src/task rc:$rc 1 modify /test/TEST/ 2>&1};
qx{../src/task rc:$rc 1 modify "/is //" 2>&1};
$output = qx{../src/task rc:$rc info 1 2>&1};
like ($output, qr/ID\s+1\n/, "$ut: add ID");
like ($output, qr/Status\s+Pending\n/, "$ut: add Pending");
like ($output, qr/Description\s+This a TEST\n/, "$ut: add Description");
code, out, err = self.t(("info", "1"))
# Cleanup.
unlink qw(pending.data completed.data undo.data backlog.data), $rc;
exit 0;
self.assertRegexpMatches(out, "ID\s+1\n")
self.assertRegexpMatches(out, "Description\s+This is a test\n")
self.assertRegexpMatches(out, "Status\s+Pending\n")
self.assertRegexpMatches(out, "UUID\s+" + UUID_regex + "\n")
def test_modify_slash(self):
"Test the /// modifier"
self.t(("1", "modify", "/test/TEST/"))
self.t(("1", "modify", "/is //"))
code, out, err = self.t(("info", "1"))
self.assertRegexpMatches(out, "ID\s+1\n")
self.assertRegexpMatches(out, "Status\s+Pending\n")
self.assertRegexpMatches(out, "Description\s+This a TEST\n")
self.assertRegexpMatches(out, "UUID\s+" + UUID_regex + "\n")
if __name__ == "__main__":
from simpletap import TAPTestRunner
unittest.main(testRunner=TAPTestRunner())
# vim: ai sts=4 et sw=4

View File

@@ -1,23 +1,28 @@
# -*- coding: utf-8 -*-
import signal
sig_names = dict((k, v) for v, k in reversed(sorted(signal.__dict__.items()))
if v.startswith('SIG') and not v.startswith('SIG_'))
class CommandError(Exception):
def __init__(self, cmd, code, out, err=None, msg=None):
DEFAULT = ("Command '{{0}}' was {signal}'ed. "
"SIGABRT usually means task timed out.\n")
if msg is None:
msg_suffix = "\n*** Start STDOUT ***\n{2}\n*** End STDOUT ***\n"
if err is not None:
msg_suffix += (
"\n*** Start STDERR ***\n{3}\n*** End STDERR ***\n"
)
if code == -signal.SIGABRT:
self.msg = ("Command '{0}' was aborted, likely due to not "
"finishing in due time. The exit code was '{1}'.\n"
) + msg_suffix
if code < 0:
self.msg = DEFAULT.format(signal=sig_names[abs(code)])
else:
self.msg = ("Command '{0}' finished with unexpected exit "
"code '{1}'.\n"
) + msg_suffix
"code '{1}'.\n")
self.msg += msg_suffix
else:
self.msg = msg
@@ -34,12 +39,13 @@ class HookError(Exception):
pass
class TimeoutWaitingForStream(object):
class TimeoutWaitingFor(object):
def __init__(self, name):
self.stream = name
self.name = name
def __repr__(self):
return "*** Timeout reached while waiting for %s ***".format(self.name)
return "*** Timeout reached while waiting for {0} ***".format(
self.name)
class StreamsAreMerged(object):

View File

@@ -16,6 +16,26 @@ from .utils import DEFAULT_HOOK_PATH
from .exceptions import HookError
class InvalidJSON(object):
"""Object representing the original unparsed JSON string and the JSON error
"""
def __init__(self, original, error):
self.original = original
self.error = error
def json_decoder(string):
"""Attempt to decode a JSON string and in case of error return an
InvalidJSON object
"""
decoder = json.JSONDecoder().decode
try:
return decoder(string)
except ValueError as e:
return InvalidJSON(string, str(e))
class Hooks(object):
"""Abstraction to help interact with hooks (add, remove) during tests and
keep track of which are active.
@@ -51,10 +71,10 @@ class Hooks(object):
def __getitem__(self, name):
return self._hooks[name]
def __setitem(self, key, value):
def __setitem__(self, key, value):
self._hooks[key] = value
def __delitem(self, key):
def __delitem__(self, key):
del self._hooks[key]
def __iter__(self):
@@ -81,6 +101,8 @@ class Hooks(object):
else:
self[hookname] = Hook(hookname, self.hookdir, content)
self[hookname].enable()
def add_default(self, hookname, log=False):
"""Register a pre-built hook that exists in the folder containing hooks
used for testing.
@@ -94,6 +116,9 @@ class Hooks(object):
else:
self[hookname] = Hook(hookname, self.hookdir, default=True)
# Finally enable this hook
self[hookname].enable()
def remove(self, hook):
"""Remove the hook matching given hookname"""
try:
@@ -178,9 +203,6 @@ class Hook(object):
with open(self.hookfile, 'w') as fh:
fh.write(content)
# Finally enable the hook
self.enable()
def __eq__(self, other):
try:
if self.hookname == other.hookname:
@@ -292,9 +314,6 @@ class LoggedHook(Hook):
# Setup wrapper pointing to the correct hook name
self._setup_wrapper()
# Finally enable all hooks
self.enable()
def __repr__(self):
return "<LoggedHook '{0}'>".format(self.hookname)
@@ -349,8 +368,30 @@ class LoggedHook(Hook):
# Cache is up to date
return True
def _parse_log(self):
"""Parse the logs generated by the hook
def enable(self):
"""Make hookfile executable to allow triggering
"""
super(LoggedHook, self).enable()
os.chmod(self.wrappedfile, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC)
def disable(self):
"""Remove hookfile executable bit to deny triggering
"""
super(LoggedHook, self).disable()
os.chmod(self.wrappedfile, stat.S_IREAD | stat.S_IWRITE)
def is_active(self):
"""Check if hook is active by verifying the execute bit
"""
parent_is_active = super(LoggedHook, self).disable()
return parent_is_active and os.access(self.wrappedfile, os.X_OK)
def get_logs(self):
"""Parse the logs generated by the hook and return a dictionary
containing the logs collected with the wrapper in a python friendly
format:
* JSON is parsed as python dictionaries
* timestamps are parsed as datetime objects
It should look something like this:
@@ -381,111 +422,91 @@ class LoggedHook(Hook):
with open(self.hooklog_in) as fh:
for i, line in enumerate(fh):
line = line.rstrip("\n")
if line.startswith("%"):
# Timestamp includes nanosecond resolution
timestamp = line.split(" ")[-1]
log["calls"].append(timestamp)
# convert timestamp to python datetime object
log["calls"].append(
datetime.fromtimestamp(float(timestamp))
)
elif line.startswith("{"):
log["input"]["json"].append(line)
# Decode json input (to hook)
log["input"]["json"].append(json_decoder(line))
else:
raise IOError("Unexpected content on STDIN line {0}: {1}"
.format(i, line))
with open(self.hooklog_out) as fh:
for line in fh:
line = line.rstrip("\n")
if line.startswith("!"):
exitcode = int(line.split(" ")[-1])
log["exitcode"] = exitcode
elif line.startswith("{"):
log["output"]["json"].append(line)
# Decode json output (from hook)
log["output"]["json"].append(json_decoder(line))
else:
log["output"]["msgs"].append(line)
# NOTE convert all lists to tuples to prevent tampering?
self._cache["log"] = log
# Update last modification timestamp in cache
self._cache["last_change"] = self._get_log_stat()
def enable(self):
"""Make hookfile executable to allow triggering
"""
super(LoggedHook, self).enable()
os.chmod(self.wrappedfile, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC)
return self._cache["log"]
def disable(self):
"""Remove hookfile executable bit to deny triggering
"""
super(LoggedHook, self).disable()
os.chmod(self.wrappedfile, stat.S_IREAD | stat.S_IWRITE)
def is_active(self):
"""Check if hook is active by verifying the execute bit
"""
parent_is_active = super(LoggedHook, self).disable()
return parent_is_active and os.access(self.wrappedfile, os.X_OK)
def get_logs(self):
"""Return a dictionary containing the logs collected with the wrapper
in a python friendly format:
* JSON is parsed as python dictionaries
* timestamps are parsed as datetime objects
"""
log = self._parse_log()
newlog = {}
json_decoder = json.JSONDecoder().decode
for k1 in log:
# Timestamps
if k1 == "calls":
timestamp = lambda x: datetime.fromtimestamp(int(x) / 1e9)
newlog[k1] = map(timestamp, log[k1])
elif k1 in ("input", "output"):
newlog[k1] = {}
for k2 in log[k1]:
if k2 == "json":
# JSON replies
newlog[k1][k2] = map(json_decoder, log[k1][k2])
else:
# Messages and future fields
newlog[k1][k2] = deepcopy(log[k1][k2])
else:
# exitcode and future fields
newlog[k1] = deepcopy(log[k1])
return newlog
def assert_triggered(self):
"""Check if current hook file was triggered/used by taskwarrior
"""
log = self._parse_log()
if log["calls"]:
return True
else:
return False
def assert_triggered_count(self, count):
def assertTriggeredCount(self, count):
"""Check if current hook file was triggered/used by taskwarrior and
how many times.
"""
log = self._parse_log()
log = self.get_logs()
if len(log["calls"]) == count:
return True
else:
return False
assert len(log["calls"]) == count, ("{0} calls expected for {1} but "
"found {2}".format(
count,
self.hookname,
log["calls"]
))
def assert_exitcode(self, exitcode):
def assertExitcode(self, exitcode):
"""Check if current hook finished with the expected exit code
"""
log = self._parse_log()
log = self.get_logs()
if log["exitcode"] == exitcode:
return True
else:
return False
assert log["exitcode"] == exitcode, ("Expected exit code {0} for {1} "
"but found {2}".format(
exitcode,
self.hookname,
log["exitcode"]
))
def assertValidJSONOutput(self):
"""Check if current hook output is valid JSON in all expected replies
"""
log = self.get_logs()
for i, out in enumerate(log["output"]["json"]):
assert not isinstance(out, InvalidJSON), ("Invalid JSON found at "
"reply number {0} with "
"content {1}".format(
i + 1,
out.original
))
def assertInvalidJSONOutput(self):
"""Check if current hook output is invalid JSON in any expected reply
"""
log = self.get_logs()
for i, out in enumerate(log["output"]["json"]):
assert isinstance(out, InvalidJSON), ("Valid JSON found at reply "
"number {0} with content "
"{1}".format(
i + 1,
out.original
))
# vim: ai sts=4 et sw=4

View File

@@ -33,7 +33,7 @@ class Taskd(object):
DEFAULT_TASKD = binary_location("taskd")
def __init__(self, taskd=DEFAULT_TASKD, certpath=None,
address="127.0.0.1"):
address="localhost"):
"""Initialize a Task server that runs in the background and stores data
in a temporary folder
@@ -60,7 +60,7 @@ class Taskd(object):
self.certpath = certpath
self.address = address
self.port = find_unused_port()
self.port = find_unused_port(self.address)
# Keep all certificate paths public for access by TaskClients
self.client_cert = os.path.join(self.certpath, "client.cert.pem")
@@ -182,7 +182,7 @@ class Taskd(object):
if self.proc.poll() is not None:
return False
if not port_used(port=self.port):
if not port_used(addr=self.address, port=self.port):
return False
return True

View File

@@ -13,7 +13,7 @@ try:
import simplejson as json
except ImportError:
import json
from .exceptions import CommandError, TimeoutWaitingForStream
from .exceptions import CommandError, TimeoutWaitingFor
USED_PORTS = set()
ON_POSIX = 'posix' in sys.builtin_module_names
@@ -43,6 +43,8 @@ TASKD_SKIP = os.environ.get("TASKD_SKIP", False)
# Environment flags to control use of PATH or in-tree binaries
USE_PATH = os.environ.get("USE_PATH", False)
UUID_regex = ("[0-9A-Fa-f]{8}-" + ("[0-9A-Fa-f]{4}-" * 3) + "[0-9A-Fa-f]{12}")
def binary_location(cmd):
"""If USE_PATH is set rely on PATH to look for task/taskd binaries.
@@ -54,76 +56,142 @@ def binary_location(cmd):
return os.path.join(BIN_PREFIX, cmd)
def wait_process(proc, timeout=1):
"""Wait for process to finish
def wait_condition(cond, timeout=1):
"""Wait for condition to return anything other than None
"""
if timeout is None:
timeout = 1
sleeptime = .1
# NOTE Increasing sleeptime can dramatically increase testsuite runtime
# It also reduces CPU load significantly
sleeptime = .01
if timeout < sleeptime:
print("Warning, timeout cannot be smaller than", sleeptime)
timeout = sleeptime
# Max number of attempts until giving up
tries = int(timeout / sleeptime)
# Wait for up to a second for the process to finish and avoid zombies
for i in range(tries):
exit = proc.poll()
val = cond()
if exit is not None:
if val is not None:
break
sleep(sleeptime)
return exit
return val
def _get_output(proc, input, timeout=None):
def wait_process(pid, timeout=None):
"""Wait for process to finish
"""
def process():
try:
os.kill(pid, 0)
except OSError:
# Process is dead
return True
else:
# Process is still ticking
return None
return wait_condition(process, timeout)
def _queue_output(arguments, pidq, outputq):
"""Read/Write output/input of given process.
This function is meant to be executed in a thread as it may block
"""
kwargs = arguments["process"]
input = arguments["input"]
proc = Popen(**kwargs)
# NOTE If for whatever reason pid is None at the time of access, use the
# following line instead
# pid = wait_condition(lambda: proc.pid)
pid = proc.pid
# Put the PID in the queue for main process to know
pidq.put(pid)
# Send input and wait for finish
out, err = proc.communicate(input)
# Give the output back to the caller
outputq.put((out, err, proc.returncode))
def _retrieve_output(thread, timeout, queue, thread_error):
"""Fetch output from taskw subprocess queues
"""
# Try to join the thread on failure abort
thread.join(timeout)
if thread.isAlive():
# Join should have killed the thread. This is unexpected
raise TimeoutWaitingFor(thread_error + ". Unexpected error")
# Thread died so we should have output
try:
# data = (stdout, stderr, exitcode)
data = queue.get(timeout=timeout)
except Empty:
data = TimeoutWaitingFor("streams from TaskWarrior")
return data
def _get_output(arguments, timeout=None):
"""Collect output from the subprocess without blocking the main process if
subprocess hangs.
"""
def queue_output(proc, input, outq, errq):
"""Read/Write output/input of given process.
This function is meant to be executed in a thread as it may block
"""
# Send input and wait for finish
out, err = proc.communicate(input)
# Give the output back to the caller
outq.put(out)
errq.put(err)
# NOTE Increase this value if tests fail with None being received as
# stdout/stderr instead of the expected content
output_timeout = 0.1 # seconds
outq = Queue()
errq = Queue()
pidq = Queue()
outputq = Queue()
t = Thread(target=queue_output, args=(proc, input, outq, errq))
t = Thread(target=_queue_output, args=(arguments, pidq, outputq))
t.daemon = True
t.start()
# A task process shouldn't take longer than 1 second to finish
exit = wait_process(proc, timeout)
try:
pid = pidq.get(timeout=timeout)
except Empty:
return _retrieve_output(t, output_timeout, outputq,
"TaskWarrior to start")
# If it does take longer than 1 second, abort it
if exit is None:
# Wait for process to finish (normal execution)
state = wait_process(pid, timeout)
if state:
# Process finished
return _retrieve_output(t, output_timeout, outputq,
"TaskWarrior thread to join")
# If we reach this point we assume the process got stuck or timed out
for sig in (signal.SIGABRT, signal.SIGTERM, signal.SIGKILL):
# Start with lower signals and escalate if process ignores them
try:
proc.send_signal(signal.SIGABRT)
os.kill(pid, signal.SIGABRT)
except OSError as e:
# 3 means the process finished/died between last check and now
if e.errno != 3:
raise
exit = wait_process(proc)
# NOTE Increase this value if tests fail with None being received as
# stdout/stderr instead of the expected content
timeout = 0.1 # seconds
# Wait for process to finish (should die/exit after signal)
state = wait_process(pid, timeout)
try:
out = outq.get(timeout=timeout)
except Empty:
out = TimeoutWaitingForStream("stdout")
try:
err = errq.get(timeout=timeout)
except Empty:
err = TimeoutWaitingForStream("stderr")
if state:
# Process finished
return _retrieve_output(t, output_timeout, outputq,
"TaskWarrior to die")
return out, err, exit
# This should never happen but in case something goes really bad
raise OSError("TaskWarrior stopped responding and couldn't be killed")
def run_cmd_wait(cmd, input=None, stdout=PIPE, stderr=PIPE,
@@ -140,9 +208,19 @@ def run_cmd_wait(cmd, input=None, stdout=PIPE, stderr=PIPE,
else:
stderr = PIPE
p = Popen(cmd, stdin=stdin, stdout=stdout, stderr=stderr, bufsize=1,
close_fds=ON_POSIX, env=env)
out, err, exit = _get_output(p, input, timeout)
arguments = {
"process": {
"args": cmd,
"stdin": stdin,
"stdout": stdout,
"stderr": stderr,
"bufsize": 1,
"close_fds": ON_POSIX,
"env": env,
},
"input": input,
}
out, err, exit = _get_output(arguments, timeout)
if merge_streams:
if exit != 0:
@@ -164,16 +242,40 @@ def run_cmd_wait_nofail(*args, **kwargs):
return e.code, e.out, e.err
def get_IPs(hostname):
output = {}
addrs = socket.getaddrinfo(hostname, 0, 0, 0, socket.IPPROTO_TCP)
for family, socktype, proto, canonname, sockaddr in addrs:
addr = sockaddr[0]
output[family] = addr
return output
def port_used(addr="localhost", port=None):
"Return True if port is in use, False otherwise"
if port is None:
raise TypeError("Argument 'port' may not be None")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
result = s.connect_ex((addr, port))
s.close()
# result == 0 if connection was successful
return result == 0
# If we got an address name, resolve it both to IPv6 and IPv4.
IPs = get_IPs(addr)
# Taskd seems to prefer IPv6 so we do it first
for family in (socket.AF_INET6, socket.AF_INET):
try:
addr = IPs[family]
except KeyError:
continue
s = socket.socket(family, socket.SOCK_STREAM)
result = s.connect_ex((addr, port))
s.close()
if result == 0:
# connection was successful
return True
else:
return False
def find_unused_port(addr="localhost", start=53589, track=True):
@@ -248,8 +350,8 @@ except ImportError:
# Additionally check that `file` is not a directory, as on Windows
# directories pass the os.access check.
def _access_check(fn, mode):
return (os.path.exists(fn) and os.access(fn, mode)
and not os.path.isdir(fn))
return (os.path.exists(fn) and os.access(fn, mode) and
not os.path.isdir(fn))
# If we're given a path with a directory part, look it up directly
# rather than referring to PATH directories. This includes checking

View File

@@ -1,70 +1,87 @@
#! /usr/bin/env perl
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
###############################################################################
#
# Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# http://www.opensource.org/licenses/mit-license.php
#
###############################################################################
use strict;
use warnings;
use Test::More tests => 4;
import sys
import os
import unittest
# Ensure python finds the local simpletap module
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
# Ensure environment has no influence.
delete $ENV{'TASKDATA'};
delete $ENV{'TASKRC'};
from basetest import Task, TestCase
use File::Basename;
my $ut = basename ($0);
my $rc = $ut . '.rc';
# Create the rc file.
if (open my $fh, '>', $rc)
{
print $fh "data.location=.\n",
"verbose=affected\n";
close $fh;
}
class TestBug1006(TestCase):
"""Bug with completion of "des" in task descriptions and annotations. It
happens for all the shortcuts for column attributes that are automatically
completed. This is because DOM elements are checked before standard words
when strings are tokenized.
"""
def setUp(self):
self.t = Task()
self.t.config("verbose", "affected")
# Bug with completion of "des" in task descriptions and annotations. It happens
# for all the shortcuts for column attributes that are automatically completed.
# This is because DOM elements are checked before standard words when strings
# are tokenized.
def initial_tasks(self):
self.t(("add", "des"))
self.t(("1", "annotate", "des"))
# Check that the completion is inactive in task descriptions
qx{../src/task rc:$rc add des 2>&1};
qx{../src/task rc:$rc 1 annotate des 2>&1};
my $output = qx{../src/task rc:$rc 1 info 2>&1};
like ($output, qr/^Description\s+des$/ms, "$ut: Attribute not completed in description");
unlike ($output, qr/description/ms, "$ut: Attribute not completed in description");
def test_completion_of_des_inactive(self):
"Check that the completion is inactive in task descriptions"
# Check that the completion works when needed
$output = qx{../src/task rc:$rc des:des 2>&1};
like ($output, qr/^1 task$/ms, "$ut: Task found using its description");
self.initial_tasks()
code, out, err = self.t(("1", "info"))
qx{../src/task rc:$rc add entrée interdite 2>&1};
$output = qx{../src/task rc:$rc list interdite 2>&1};
like ($output, qr/entrée interdite/, "$ut: 'entrée' left intact");
expected = "Description +des\n"
errormsg = "Attribute not completed in description"
self.assertRegexpMatches(out, expected, msg=errormsg)
# Cleanup.
unlink qw(pending.data completed.data undo.data backlog.data), $rc;
exit 0;
notexpected = "description"
self.assertNotIn(notexpected, out, msg=errormsg)
def test_completion_as_expected(self):
"Check that the completion works when needed"
self.initial_tasks()
code, out, err = self.t(("des:des",))
errormsg = "Task found using its description"
self.assertIn("1 task", out, msg=errormsg)
def test_accented_chars(self):
"Check that é in entrée remains untouched"
self.t(("add", "entrée interdite"))
code, out, err = self.t(("list", "interdite"))
errormsg = "'entrée' left intact"
self.assertIn("entrée interdite", out, msg=errormsg)
if __name__ == "__main__":
from simpletap import TAPTestRunner
unittest.main(testRunner=TAPTestRunner())
# vim: ai sts=4 et sw=4

View File

@@ -46,10 +46,9 @@ if (open my $fh, '>', $rc)
}
# Bug 982: Missing words when adding a description with NUMBER-SOMETHING
qx{../src/task rc:$rc add 1-test 1+tag 2>&1};
my $output = qx{../src/task rc:$rc 1 info 2>&1};
like ($output, qr/^Description 1-test 1\+tag$/ms, 'Description contains plus and minus signs');
like ($output, qr/^Description\s+1-test 1\+tag$/ms, "$ut: Description contains plus and minus signs");
# Cleanup.
unlink qw(pending.data completed.data undo.data backlog.data), $rc;

View File

@@ -1,121 +0,0 @@
#! /usr/bin/env perl
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
use strict;
use warnings;
use Test::More tests => 7;
# Ensure environment has no influence.
delete $ENV{'TASKDATA'};
delete $ENV{'TASKRC'};
use File::Basename;
my $ut = basename ($0);
my $rc = $ut . '.rc';
# Create the rc file.
if (open my $fh, '>', $rc)
{
print $fh "data.location=.\n",
"confirmation=off\n";
close $fh;
}
# Bug 987 - 'Total active time' is still increasing when a task is done
# Test all cases for which the active duration must be close to zero and
# constant:
# - start (a), stop (o), done (d) and none (n)
# (none disallows the name of each task to be the prefix of any other)
qx{../src/task rc:$rc add test_aodn};
qx{../src/task rc:$rc test_aodn start};
qx{faketime -f '+1s' ../src/task rc:$rc test_aodn stop};
qx{faketime -f '+2s' ../src/task rc:$rc test_aodn done};
check_constant_close_zero("test_aodn");
# - start and done
qx{../src/task rc:$rc add test_adn};
qx{../src/task rc:$rc test_adn start};
qx{faketime -f '+1s' ../src/task rc:$rc test_adn done};
check_constant_close_zero("test_adn");
# - start, done and stop
qx{../src/task rc:$rc add test_adon};
qx{../src/task rc:$rc test_adon start};
qx{faketime -f '+1s' ../src/task rc:$rc test_adon done};
qx{faketime -f '+2s' ../src/task rc:$rc test_adon stop};
check_constant_close_zero("test_adon");
# - done, start and stop (XXX whether this should be allowed is questionable)
qx{../src/task rc:$rc add test_daon};
qx{../src/task rc:$rc test_daon done};
qx{faketime -f '+1s' ../src/task rc:$rc test_daon start};
qx{faketime -f '+2s' ../src/task rc:$rc test_daon stop};
check_constant_close_zero("test_daon");
# Test all cases for which the active duration must not be shown:
# - done
qx{../src/task rc:$rc add test_dn};
qx{../src/task rc:$rc test_dn done};
check_no_summary("test_dn");
# Test all case for which the active duration must be close to zero and
# increasing:
# - start
qx{../src/task rc:$rc add test_sn};
qx{../src/task rc:$rc test_sn start};
check_running_close_zero("test_sn");
# - done and start (XXX whether this should be allowed is questionable)
qx{../src/task rc:$rc add test_dsn};
qx{../src/task rc:$rc test_dsn start};
check_running_close_zero("test_dsn");
unlink qw(pending.data completed.data undo.data backlog.data), $rc;
exit 0;
################################################################################
# Check that the active time is not above one minute after waiting for one minute
sub check_constant_close_zero {
my ($task) = @_;
my $output = qx{faketime -f '+60s' ../src/task rc:$rc $task info};
like ($output, qr/Total active time 0:00:/ms, 'Total active duration is not increasing and is close to zeo');
}
# Check that there is no active time summary
sub check_no_summary {
my ($task) = @_;
my $output = qx{faketime -f '+10s' ../src/task rc:$rc $task info};
unlike ($output, qr/Total active time/ms, 'No total active duration summary');
}
# Check that the active time is above one minute after waiting for one minute
sub check_running_close_zero {
my ($task) = @_;
my $output = qx{faketime -f '+60s' ../src/task rc:$rc $task info};
like ($output, qr/Total active time 0:01:/ms, 'Total active duration is increasing and is close to zero');
}

View File

@@ -29,7 +29,7 @@
use strict;
use warnings;
use Test::More tests => 81;
use Test::More tests => 78;
# Ensure environment has no influence.
delete $ENV{'TASKDATA'};
@@ -51,7 +51,8 @@ if (open my $fh, '>', $rc)
"color.calendar.overdue=black on red\n",
"color.calendar.weekend=white on bright black\n",
"confirmation=off\n",
"bulk=10\n";
"bulk=10\n",
"verbose=none\n";
close $fh;
}
@@ -181,6 +182,7 @@ if (open my $fh, '>', $rc)
"calendar.details.report=list\n",
"calendar.holidays=full\n",
"color=on\n",
"_forcecolor=on\n",
"color.alternate=\n",
"color.calendar.weekend=\n",
"color.calendar.holiday=black on bright yellow\n",
@@ -240,9 +242,7 @@ qx{../src/task rc:$rc add due:$duedate nine 2>&1};
$output = qx{../src/task rc:$rc calendar rc.monthsperline:1 2>&1};
like ($output, qr/$month\S*?\s+?$year/, "$ut: Current month and year are displayed");
like ($output, qr/$duedate/, "$ut: Due date on current day is displayed");
# TODO: This test fails on 2015-01-01.
like ($output, qr/[12] task/, "$ut: 1/2 due task(s) are displayed");
like ($output, qr/[123] task/, "$ut: 1/2/3 due task(s) are displayed");
$output = qx{../src/task rc:$rc calendar rc.monthsperline:1 1 2015 2>&1};
like ($output, qr/Date/, "$ut: Word Date is displayed");
@@ -258,12 +258,6 @@ $output = qx{../src/task rc:$rc calendar rc._forcecolor:on rc.monthsperline:1 rc
unlike ($output, qr/Date/, "$ut: Word Date is not displayed");
unlike ($output, qr/Holiday/, "$ut: Word Holiday is not displayed");
# TODO: This test fails on 2015-01-01.
like ($output, qr/30;103m 1/, "$ut: Holiday AAAA is color-coded");
like ($output, qr/30;103m15/, "$ut: Holiday BBBBBB is color-coded"); # 80
like ($output, qr/30;103m25/, "$ut: Holiday åäö is color-coded");
# Cleanup.
unlink qw(pending.data completed.data undo.data backlog.data), $rc;
exit 0;

11
test/conversion Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
printf "C++: %5d\n" $(ls *.t.cpp | wc -l)
printf "Python: %5d\n" $(head -n1 *.t | grep -a '\bpython' | wc -l)
printf "Perl: %5d\n" $(head -n1 *.t | grep -a '\bperl\b' | wc -l)
if [ "$1" = "-v" ]; then
echo -n "Perl left: "; echo $(grep -l '^#\! \?/usr/bin/env perl\b' *.t)
fi
echo
printf "Feature %5d\n" $(ls feature.*.t | wc -l)
printf "Bug %5d\n" $(ls {tw-,bug.}*.t | wc -l)

511
test/datetime-negative.t Executable file
View File

@@ -0,0 +1,511 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
import json
import sys
import os
import unittest
# Ensure python finds the local simpletap module
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from basetest import Task, TestCase
class BaseDateTimeNegativeTest(TestCase):
"""
Base class for Datetime negative tests.
"""
def setUp(self):
self.t = Task()
def assertInvalidDatetimeFormat(self, value):
self.t.runError(('add', 'due:%s' % value, 'test1'))
self.t.runError(('add', 'scheduled:%s' % value, 'test2'))
self.t.runError(('add', 'wait:%s' % value, 'test3'))
self.t.runError(('add', 'until:%s' % value, 'test4'))
class TestIncorrectDate(BaseDateTimeNegativeTest):
"""
This test case makes sure various formats
of incorrect datetimes do not get interpreted
as valid timetamps. Covers TW-1499.
"""
def test_set_incorrect_datetime_randomstring(self):
self.assertInvalidDatetimeFormat('random')
def test_set_incorrect_datetime_negative_in_YYYY_MM_DD(self):
self.assertInvalidDatetimeFormat('-2014-07-07')
def test_set_incorrect_datetime_missing_day_in_YYYY_MM_DD(self):
self.assertInvalidDatetimeFormat('2014-07-')
# def test_set_incorrect_datetime_month_zero_in_YYYY_MM_DD(self):
# self.assertInvalidDatetimeFormat('2014-0-12')
def test_set_incorrect_datetime_invalid_characters_in_YYYY_MM_DD(self):
self.assertInvalidDatetimeFormat('abcd-ab-ab')
# def test_set_incorrect_datetime_day_as_zeros_in_YYYY_DDD(self):
# self.assertInvalidDatetimeFormat('2014-000')
# def test_set_incorrect_datetime_overlap_day_in_nonoverlap_year_in_YYYY_DDD(self):
# self.assertInvalidDatetimeFormat('2014-366')
# def test_set_incorrect_datetime_medium_overlap_day_in_YYYY_DDD(self):
# self.assertInvalidDatetimeFormat('2014-999')
def test_set_incorrect_datetime_huge_overlap_day_in_YYYY_DDD(self):
self.assertInvalidDatetimeFormat('2014-999999999')
# def test_set_incorrect_datetime_week_with_the_number_zero_in_YYYY_Www(self):
# self.assertInvalidDatetimeFormat('2014-W00')
def test_set_incorrect_datetime_overflow_in_week_in_YYYY_Www(self):
self.assertInvalidDatetimeFormat('2014-W54')
def test_set_incorrect_datetime_day_zero_in_YYYY_WwwD(self):
self.assertInvalidDatetimeFormat('2014-W240')
def test_set_incorrect_datetime_day_eight_in_YYYY_WwwD(self):
self.assertInvalidDatetimeFormat('2014-W248')
def test_set_incorrect_datetime_day_two_hundred_in_YYYY_WwwD(self):
self.assertInvalidDatetimeFormat('2014-W24200')
# def test_set_incorrect_datetime_week_with_the_number_zero_in_YYYYWww(self):
# self.assertInvalidDatetimeFormat('2014W00')
def test_set_incorrect_datetime_overflow_in_week_in_YYYYWww(self):
self.assertInvalidDatetimeFormat('2014W54')
# def test_set_incorrect_datetime_week_zero_in_YYYYWwwD(self):
# self.assertInvalidDatetimeFormat('2014W001')
# def test_set_incorrect_datetime_fifth_day_of_week_zero_in_YYYYWwwD(self):
# self.assertInvalidDatetimeFormat('2014W005')
def test_set_incorrect_datetime_overflow_week_in_YYYYWwwD(self):
self.assertInvalidDatetimeFormat('2014W541')
def test_set_incorrect_datetime_huge_overflow_week_in_YYYYWwwD(self):
self.assertInvalidDatetimeFormat('2014W991')
# def test_set_incorrect_datetime_day_zero_in_YYYYWwwD(self):
# self.assertInvalidDatetimeFormat('2014W240')
def test_set_incorrect_datetime_day_eight_in_YYYYWwwD(self):
self.assertInvalidDatetimeFormat('2014W248')
def test_set_incorrect_datetime_day_two_hundred_in_YYYYWwwD(self):
self.assertInvalidDatetimeFormat('2014W24200')
# def test_set_incorrect_datetime_month_zero_in_YYYY_MM(self):
# self.assertInvalidDatetimeFormat('2014-00')
# def test_set_incorrect_datetime_overflow_month_in_YYYY_MM(self):
# self.assertInvalidDatetimeFormat('2014-13')
# def test_set_incorrect_datetime_huge_overflow_month_in_YYYY_MM(self):
# self.assertInvalidDatetimeFormat('2014-99')
class TestIncorrectTime(BaseDateTimeNegativeTest):
"""
This test class makes sure invalid time formats are not getting
accepted by TaskWarrior parser.
"""
# def test_set_incorrect_datetime_hour_overflow_in_hh_mm(self):
# self.assertInvalidDatetimeFormat('24:00')
def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm(self):
self.assertInvalidDatetimeFormat('99:00')
# def test_set_incorrect_datetime_minute_overflow_in_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:60')
# def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:99')
def test_set_incorrect_datetime_invalid_minutes_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:ab')
def test_set_incorrect_datetime_invalid_hours_in_hh_mm(self):
self.assertInvalidDatetimeFormat('ab:12')
def test_set_incorrect_datetime_invalid_time_in_hh_mm(self):
self.assertInvalidDatetimeFormat('ab:cd')
def test_set_incorrect_datetime_negative_hours_in_hh_mm(self):
self.assertInvalidDatetimeFormat('-12:12')
def test_set_incorrect_datetime_negative_minutes_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:-12')
# def test_set_incorrect_datetime_hour_overflow_in_hh_mmZ(self):
# self.assertInvalidDatetimeFormat('24:00Z')
def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mmZ(self):
self.assertInvalidDatetimeFormat('99:00Z')
# def test_set_incorrect_datetime_minute_overflow_in_hh_mmZ(self):
# self.assertInvalidDatetimeFormat('12:60Z')
# def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mmZ(self):
# self.assertInvalidDatetimeFormat('12:99Z')
def test_set_incorrect_datetime_invalid_minutes_in_hh_mmZ(self):
self.assertInvalidDatetimeFormat('12:abZ')
def test_set_incorrect_datetime_invalid_hours_in_hh_mmZ(self):
self.assertInvalidDatetimeFormat('ab:12Z')
def test_set_incorrect_datetime_invalid_time_in_hh_mmZ(self):
self.assertInvalidDatetimeFormat('ab:cdZ')
def test_set_incorrect_datetime_negative_hours_in_hh_mmZ(self):
self.assertInvalidDatetimeFormat('-12:12Z')
def test_set_incorrect_datetime_negative_minutes_in_hh_mmZ(self):
self.assertInvalidDatetimeFormat('12:-12Z')
# def test_set_incorrect_datetime_hour_overflow_in_hh_mm_plus_hh_mm(self):
# self.assertInvalidDatetimeFormat('24:00+01:00')
def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('99:00+01:00')
# def test_set_incorrect_datetime_minute_overflow_in_hh_mm_plus_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:60+01:00')
# def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm_plus_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:99+01:00')
def test_set_incorrect_datetime_invalid_minutes_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:ab+01:00')
def test_set_incorrect_datetime_invalid_hours_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('ab:12+01:00')
def test_set_incorrect_datetime_invalid_time_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('ab:cd+01:00')
def test_set_incorrect_datetime_negative_hours_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('-12:12+01:00')
def test_set_incorrect_datetime_negative_minutes_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:-12+01:00')
# def test_set_incorrect_datetime_hour_overflow_in_hh_mm_plus_hh_mm(self):
# self.assertInvalidDatetimeFormat('24:00-01:00')
def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('99:00-01:00')
# def test_set_incorrect_datetime_minute_overflow_in_hh_mm_plus_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:60-01:00')
# def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm_plus_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:99-01:00')
def test_set_incorrect_datetime_invalid_minutes_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:ab-01:00')
def test_set_incorrect_datetime_invalid_hours_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('ab:12-01:00')
def test_set_incorrect_datetime_invalid_time_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('ab:cd-01:00')
def test_set_incorrect_datetime_negative_hours_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('-12:12-01:00')
def test_set_incorrect_datetime_negative_minutes_in_hh_mm_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:-12-01:00')
# def test_set_incorrect_datetime_hour_overflow_in_hh_mm_ss(self):
# self.assertInvalidDatetimeFormat('24:00:00')
def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('99:00:00')
# def test_set_incorrect_datetime_minute_overflow_in_hh_mm_ss(self):
# self.assertInvalidDatetimeFormat('12:60:00')
# def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm_ss(self):
# self.assertInvalidDatetimeFormat('12:99:00')
# def test_set_incorrect_datetime_second_overflow_in_hh_mm_ss(self):
# self.assertInvalidDatetimeFormat('12:12:60')
# def test_set_incorrect_datetime_huge_second_overflow_in_hh_mm_ss(self):
# self.assertInvalidDatetimeFormat('12:12:99')
def test_set_incorrect_datetime_invalid_minutes_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('12:ab:00')
def test_set_incorrect_datetime_invalid_hours_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('ab:12:00')
def test_set_incorrect_datetime_invalid_seconds_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('12:12:ab')
def test_set_incorrect_datetime_invalid_time_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('ab:cd:ef')
def test_set_incorrect_datetime_negative_hours_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('-12:12:12')
def test_set_incorrect_datetime_negative_minutes_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('12:-12:12')
# def test_set_incorrect_datetime_negative_seconds_in_hh_mm_ss(self):
# self.assertInvalidDatetimeFormat('12:12:-12')
# def test_set_incorrect_datetime_hour_overflow_in_hh_mm_ssZ(self):
# self.assertInvalidDatetimeFormat('24:00:00Z')
def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm_ssZ(self):
self.assertInvalidDatetimeFormat('99:00:00Z')
# def test_set_incorrect_datetime_minute_overflow_in_hh_mm_ssZ(self):
# self.assertInvalidDatetimeFormat('12:60:00Z')
# def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm_ssZ(self):
# self.assertInvalidDatetimeFormat('12:99:00Z')
# def test_set_incorrect_datetime_second_overflow_in_hh_mm_ssZ(self):
# self.assertInvalidDatetimeFormat('12:12:60Z')
# def test_set_incorrect_datetime_huge_second_overflow_in_hh_mm_ssZ(self):
# self.assertInvalidDatetimeFormat('12:12:99Z')
def test_set_incorrect_datetime_invalid_minutes_in_hh_mm_ssZ(self):
self.assertInvalidDatetimeFormat('12:ab:00Z')
def test_set_incorrect_datetime_invalid_hours_in_hh_mm_ssZ(self):
self.assertInvalidDatetimeFormat('ab:12:00Z')
def test_set_incorrect_datetime_invalid_seconds_in_hh_mm_ssZ(self):
self.assertInvalidDatetimeFormat('12:12:abZ')
def test_set_incorrect_datetime_invalid_time_in_hh_mm_ssZ(self):
self.assertInvalidDatetimeFormat('ab:cd:efZ')
def test_set_incorrect_datetime_negative_hours_in_hh_mm_ssZ(self):
self.assertInvalidDatetimeFormat('-12:12:12Z')
def test_set_incorrect_datetime_negative_minutes_in_hh_mm_ssZ(self):
self.assertInvalidDatetimeFormat('12:-12:12Z')
def test_set_incorrect_datetime_negative_seconds_in_hh_mm_ssZ(self):
self.assertInvalidDatetimeFormat('12:12:-12Z')
# def test_set_incorrect_datetime_hour_overflow_in_hh_mm_ss_plus_hh_mm(self):
# self.assertInvalidDatetimeFormat('24:00:00+01:00')
def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm_ss_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('95:00:00+01:00')
# def test_set_incorrect_datetime_minute_overflow_in_hh_mm_ss_plus_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:60:00+01:00')
# def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm_ss_plus_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:99:00+01:00')
# def test_set_incorrect_datetime_second_overflow_in_hh_mm_ss_plus_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:12:60+01:00')
# def test_set_incorrect_datetime_huge_second_overflow_in_hh_mm_ss_plus_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:12:99+01:00')
def test_set_incorrect_datetime_invalid_minutes_in_hh_mm_ss_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:ab:00+01:00')
def test_set_incorrect_datetime_invalid_hours_in_hh_mm_ss_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('ab:12:00+01:00')
def test_set_incorrect_datetime_invalid_seconds_in_hh_mm_ss_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12:ab+01:00')
def test_set_incorrect_datetime_invalid_time_in_hh_mm_ss_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('ab:cd:ef+01:00')
def test_set_incorrect_datetime_negative_hours_in_hh_mm_ss_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('-12:12:12+01:00')
def test_set_incorrect_datetime_negative_minutes_in_hh_mm_ss_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:-12:12+01:00')
def test_set_incorrect_datetime_negative_seconds_in_hh_mm_ss_plus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12:-12+01:00')
# def test_set_incorrect_datetime_hour_overflow_in_hh_mm_ss_minus_hh_mm(self):
# self.assertInvalidDatetimeFormat('24:00:00-01:00')
def test_set_incorrect_datetime_huge_hour_overflow_in_hh_mm_ss_minus_hh_mm(self):
self.assertInvalidDatetimeFormat('95:00:00-01:00')
# def test_set_incorrect_datetime_minute_overflow_in_hh_mm_ss_minus_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:60:00-01:00')
# def test_set_incorrect_datetime_huge_minute_overflow_in_hh_mm_ss_minus_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:99:00-01:00')
# def test_set_incorrect_datetime_second_overflow_in_hh_mm_ss_minus_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:12:60-01:00')
# def test_set_incorrect_datetime_huge_second_overflow_in_hh_mm_ss_minus_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:12:99-01:00')
def test_set_incorrect_datetime_invalid_minutes_in_hh_mm_ss_minus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:ab:00-01:00')
def test_set_incorrect_datetime_invalid_hours_in_hh_mm_ss_minus_hh_mm(self):
self.assertInvalidDatetimeFormat('ab:12:00-01:00')
def test_set_incorrect_datetime_invalid_seconds_in_hh_mm_ss_minus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12:ab-01:00')
def test_set_incorrect_datetime_invalid_time_in_hh_mm_ss_minus_hh_mm(self):
self.assertInvalidDatetimeFormat('ab:cd:ef-01:00')
def test_set_incorrect_datetime_negative_hours_in_hh_mm_ss_minus_hh_mm(self):
self.assertInvalidDatetimeFormat('-12:12:12-01:00')
def test_set_incorrect_datetime_negative_minutes_in_hh_mm_ss_minus_hh_mm(self):
self.assertInvalidDatetimeFormat('12:-12:12-01:00')
# def test_set_incorrect_datetime_negative_seconds_in_hh_mm_ss_minus_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:12:-12-01:00')
# def test_set_incorrect_datetime_invalid_hour_positive_offset_in_hh_mm_ss(self):
# self.assertInvalidDatetimeFormat('12:12:12+13:00')
# def test_set_incorrect_datetime_invalid_medium_hour_positive_offset_in_hh_mm_ss(self):
# self.assertInvalidDatetimeFormat('12:12:12+24:00')
def test_set_incorrect_datetime_invalid_huge_hour_positive_offset_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('12:12:12+99:00')
# def test_set_incorrect_datetime_invalid_minute_positive_offset_in_hh_mm_ss(self):
# self.assertInvalidDatetimeFormat('12:12:12+03:60')
# def test_set_incorrect_datetime_invalid_huge_minute_positive_offset_in_hh_mm_ss(self):
# self.assertInvalidDatetimeFormat('12:12:12+03:99')
def test_set_incorrect_datetime_invalid_hour_positive_offset_length_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('12:12:12+3:20')
def test_set_incorrect_datetime_invalid_minute_positive_offset_lentgh_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('12:12:12+03:2')
def test_set_incorrect_datetime_invalid_positive_offset_length_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('12:12:12+3:2')
# def test_set_incorrect_datetime_invalid_hour_negative_offset_in_hh_mm_ss(self):
# self.assertInvalidDatetimeFormat('12:12:12-13:00')
# def test_set_incorrect_datetime_invalid_medium_hour_negative_offset_in_hh_mm_ss(self):
# self.assertInvalidDatetimeFormat('12:12:12-24:00')
def test_set_incorrect_datetime_invalid_huge_hour_negative_offset_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('12:12:12-99:00')
# def test_set_incorrect_datetime_invalid_minute_negative_offset_in_hh_mm_ss(self):
# self.assertInvalidDatetimeFormat('12:12:12-03:60')
# def test_set_incorrect_datetime_invalid_huge_minute_negative_offset_in_hh_mm_ss(self):
# self.assertInvalidDatetimeFormat('12:12:12-03:99')
def test_set_incorrect_datetime_invalid_hour_negative_offset_length_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('12:12:12-3:20')
def test_set_incorrect_datetime_invalid_minute_negative_offset_lentgh_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('12:12:12-03:2')
def test_set_incorrect_datetime_invalid_negative_offset_length_in_hh_mm_ss(self):
self.assertInvalidDatetimeFormat('12:12:12-3:2')
# def test_set_incorrect_datetime_invalid_hour_positive_offset_in_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:12+13:00')
# def test_set_incorrect_datetime_invalid_medium_hour_positive_offset_in_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:12+24:00')
def test_set_incorrect_datetime_invalid_huge_hour_positive_offset_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12+99:00')
# def test_set_incorrect_datetime_invalid_minute_positive_offset_in_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:12+03:60')
# def test_set_incorrect_datetime_invalid_huge_minute_positive_offset_in_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:12+03:99')
def test_set_incorrect_datetime_invalid_hour_positive_offset_length_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12+3:20')
def test_set_incorrect_datetime_invalid_minute_positive_offset_lentgh_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12+03:2')
def test_set_incorrect_datetime_invalid_positive_offset_length_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12+3:2')
# def test_set_incorrect_datetime_invalid_hour_negative_offset_in_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:12-13:00')
# def test_set_incorrect_datetime_invalid_medium_hour_negative_offset_in_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:12-24:00')
def test_set_incorrect_datetime_invalid_huge_hour_negative_offset_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12-99:00')
# def test_set_incorrect_datetime_invalid_minute_negative_offset_in_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:12-03:60')
# def test_set_incorrect_datetime_invalid_huge_minute_negative_offset_in_hh_mm(self):
# self.assertInvalidDatetimeFormat('12:12-03:99')
def test_set_incorrect_datetime_invalid_hour_negative_offset_length_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12-3:20')
def test_set_incorrect_datetime_invalid_minute_negative_offset_lentgh_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12-03:2')
def test_set_incorrect_datetime_invalid_negative_offset_length_in_hh_mm(self):
self.assertInvalidDatetimeFormat('12:12-3:2')
if __name__ == "__main__":
from simpletap import TAPTestRunner
unittest.main(testRunner=TAPTestRunner())
# vim: ai sts=4 et sw=4

View File

@@ -1,5 +1,30 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
import sys
import os

185
test/export.t Executable file
View File

@@ -0,0 +1,185 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
import datetime
import json
import sys
import time
import numbers
import os
import re
import unittest
# Ensure python finds the local simpletap module
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from basetest import Task, TestCase
# TODO: Move to common file with all regexpes
UUID_REGEXP = r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'
DATETIME_FORMAT = "%Y%m%dT%H%M%SZ"
class TestExportCommand(TestCase):
def setUp(self):
self.t = Task()
self.t(('add', 'test'))
def export(self, identifier):
return json.loads(self.t((str(identifier), 'export'))[1].strip())
def assertType(self, value, type):
self.assertEqual(isinstance(value, type), True)
def assertTimestamp(self, value):
"""
Asserts that timestamp is exported as string in the correct format.
"""
# Timestamps should be exported as strings
self.assertType(value, unicode)
# And they should follow the %Y%m%dT%H%M%SZ format
datetime.datetime.strptime(value, DATETIME_FORMAT)
def assertString(self, value, expected_value=None, regexp=False):
"""
Checks the type of value to be string, and that content is as passed in
the expected_value argument. This can be either a string, or a compiled
regular expression object.
"""
self.assertType(value, unicode)
if expected_value is not None:
if regexp:
# Match to pattern if checking with regexp
self.assertRegexpMatches(value, expected_value)
else:
# Equality match if checking with string
self.assertEqual(value, expected_value)
def assertNumeric(self, value, expected_value=None):
"""
Checks the type of the value to be int, and that the expected value
matches the actual value produced.
"""
self.assertType(value, numbers.Real)
if expected_value is not None:
self.assertEqual(value, expected_value)
def test_export_status(self):
self.assertString(self.export(1)['status'], "pending")
def test_export_uuid(self):
self.assertString(self.export(1)['uuid'], UUID_REGEXP, regexp=True)
def test_export_entry(self):
self.assertTimestamp(self.export(1)['entry'])
def test_export_description(self):
self.assertString(self.export(1)['description'], "test")
def test_export_start(self):
self.t(('1', 'start'))
self.assertTimestamp(self.export(1)['start'])
def test_export_end(self):
self.t(('1', 'start'))
time.sleep(2)
self.t(('1', 'done'))
self.assertTimestamp(self.export(1)['end'])
def test_export_due(self):
self.t(('1', 'modify', 'due:today'))
self.assertTimestamp(self.export(1)['due'])
def test_export_wait(self):
self.t(('1', 'modify', 'wait:tomorrow'))
self.assertTimestamp(self.export(1)['wait'])
def test_export_modified(self):
self.assertTimestamp(self.export(1)['modified'])
def test_export_scheduled(self):
self.t(('1', 'modify', 'schedule:tomorrow'))
self.assertTimestamp(self.export(1)['scheduled'])
def test_export_recur(self):
self.t(('1', 'modify', 'recur:daily', 'due:today'))
self.assertString(self.export(1)['recur'], "daily")
def test_export_project(self):
self.t(('1', 'modify', 'project:Home'))
self.assertString(self.export(1)['project'], "Home")
def test_export_priority(self):
self.t(('1', 'modify', 'priority:H'))
self.assertString(self.export(1)['priority'], "H")
def test_export_depends(self):
self.t(('add', 'everything depends on me task'))
self.t(('add', 'wrong, everything depends on me task'))
self.t(('1', 'modify', 'depends:2,3'))
values = self.export(1)['depends']
self.assertString(values)
for uuid in values.split(','):
self.assertString(uuid, UUID_REGEXP, regexp=True)
def test_export_urgency(self):
self.t(('add', 'urgent task', '+urgent'))
# Urgency can be either integer or float
self.assertNumeric(self.export(1)['urgency'])
def test_export_numeric_uda(self):
self.t.config('uda.estimate.type', 'numeric')
self.t(('add', 'estimate:42', 'test numeric uda'))
self.assertNumeric(self.export('2')['estimate'], 42)
def test_export_string_uda(self):
self.t.config('uda.estimate.type', 'string')
self.t(('add', 'estimate:big', 'test string uda'))
self.assertString(self.export('2')['estimate'], 'big')
def test_export_datetime_uda(self):
self.t.config('uda.estimate.type', 'date')
self.t(('add', 'estimate:eom', 'test date uda'))
self.assertTimestamp(self.export('2')['estimate'])
def test_export_duration_uda(self):
self.t.config('uda.estimate.type', 'duration')
self.t(('add', 'estimate:month', 'test duration uda'))
self.assertString(self.export('2')['estimate'], 'month')
if __name__ == "__main__":
from simpletap import TAPTestRunner
unittest.main(testRunner=TAPTestRunner())
# vim: ai sts=4 et sw=4

View File

@@ -1,5 +1,30 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
import sys
import os

161
test/hooks.on-add.t Executable file
View File

@@ -0,0 +1,161 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
import sys
import os
import unittest
from datetime import datetime
# Ensure python finds the local simpletap module
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from basetest import Task, TestCase
class TestHooksOnAdd(TestCase):
def setUp(self):
"""Executed before each test in the class"""
self.t = Task()
self.t.activate_hooks()
def test_onadd_builtin_accept(self):
"""on-add-accept - a well-behaved, successful, on-add hook."""
hookname = 'on-add-accept'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t(("add", "foo"))
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onadd_builtin_reject(self):
"""on-add-reject - a well-behaved, failing, on-add hook."""
hookname = 'on-add-reject'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t.runError(("add", "foo"))
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(1)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onadd_builtin_misbehave1(self):
"""on-add-misbehave1 - does not consume input."""
hookname = 'on-add-misbehave1'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t.runError(("add", "foo"))
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(1)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onadd_builtin_misbehave2(self):
"""on-add-misbehave2 - does not emit JSON."""
hookname = 'on-add-misbehave2'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t.runError(("add", "foo"))
self.assertIn("Hook Error: Expected 1 JSON task(s), found 0", err)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
def test_onadd_builtin_misbehave3(self):
"""on-add-misbehave3 - emits additional JSON."""
hookname = 'on-add-misbehave3'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t.runError(("add", "foo"))
self.assertIn("Hook Error: Expected 1 JSON task(s), found 2", err)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
def test_onadd_builtin_misbehave4(self):
"""on-add-misbehave4 - emits different task JSON."""
hookname = 'on-add-misbehave4'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t.runError(("add", "foo"))
self.assertIn("Hook Error: JSON must be for the same task:", err)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onadd_builtin_misbehave5(self):
"""on-add-misbehave5 - emits syntactically wrong JSON."""
hookname = 'on-add-misbehave5'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t.runError(("add", "foo"))
self.assertIn("Hook Error: JSON syntax error in: {\"}", err)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
hook.assertInvalidJSONOutput()
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onadd_builtin_misbehave6(self):
"""on-add-misbehave6 - emits incomplete JSON."""
hookname = 'on-add-misbehave6'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t.runError(("add", "foo"))
self.assertIn("Hook Error: JSON Object missing 'uuid' attribute.", err)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
if __name__ == "__main__":
from simpletap import TAPTestRunner
unittest.main(testRunner=TAPTestRunner())
# vim: ai sts=4 et sw=4

112
test/hooks.on-exit.t Executable file
View File

@@ -0,0 +1,112 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
import sys
import os
import unittest
from datetime import datetime
# Ensure python finds the local simpletap module
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from basetest import Task, TestCase
class TestHooksOnExit(TestCase):
def setUp(self):
"""Executed before each test in the class"""
self.t = Task()
self.t.activate_hooks()
def test_onexit_builtin_good(self):
"""on-exit-good - a well-behaved, successful, on-exit hook."""
hookname = 'on-exit-good'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t(("version",))
self.assertIn("Taskwarrior", out)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onexit_builtin_bad(self):
"""on-exit-bad - a well-behaved, failing, on-exit hook."""
hookname = 'on-exit-bad'
self.t.hooks.add_default(hookname, log=True)
# Failing hook should prevent processing.
code, out, err = self.t.runError(("version",))
self.assertIn("Taskwarrior", out)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(1)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onexit_builtin_misbehave1(self):
"""on-exit-misbehave1 - Does not consume input."""
hookname = 'on-exit-misbehave1'
self.t.hooks.add_default(hookname, log=True)
# Failing hook should prevent processing.
code, out, err = self.t(("version",))
self.assertIn("Taskwarrior", out)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onexit_builtin_misbehave2(self):
"""on-exit-misbehave2 - Emits unexpected JSON."""
hookname = 'on-exit-misbehave2'
self.t.hooks.add_default(hookname, log=True)
# Failing hook should prevent processing.
code, out, err = self.t.runError(("version",))
self.assertIn("Hook Error: Expected 0 JSON task(s), found 1", err)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
if __name__ == "__main__":
from simpletap import TAPTestRunner
unittest.main(testRunner=TAPTestRunner())
# vim: ai sts=4 et sw=4

112
test/hooks.on-launch.t Executable file
View File

@@ -0,0 +1,112 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
import sys
import os
import unittest
from datetime import datetime
# Ensure python finds the local simpletap module
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from basetest import Task, TestCase
class TestHooksOnLaunch(TestCase):
def setUp(self):
"""Executed before each test in the class"""
self.t = Task()
self.t.activate_hooks()
def test_onlaunch_builtin_good(self):
"""on-launch-good - a well-behaved, successful, on-launch hook."""
hookname = 'on-launch-good'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t(("version",))
self.assertIn("Taskwarrior", out)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onlaunch_builtin_bad(self):
"""on-launch-bad - a well-behaved, failing, on-launch hook."""
hookname = 'on-launch-bad'
self.t.hooks.add_default(hookname, log=True)
# Failing hook should prevent processing.
code, out, err = self.t.runError(("version",))
self.assertNotIn("Taskwarrior", out)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(1)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onlaunch_builtin_misbehave1(self):
"""on-launch-misbehave1 - Hook kills itself."""
hookname = 'on-launch-misbehave1'
self.t.hooks.add_default(hookname, log=True)
# Failing hook should prevent processing.
code, out, err = self.t.runError(("version",))
self.assertNotIn("Could not get Hook exit status!", err)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(137)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onlaunch_builtin_misbehave2(self):
"""on-launch-misbehave2 - Hook emits unexpected JSON."""
hookname = 'on-launch-misbehave2'
self.t.hooks.add_default(hookname, log=True)
# Failing hook should prevent processing.
code, out, err = self.t.runError(("version",))
self.assertIn("Hook Error: Expected 0 JSON task(s), found 1", err)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
if __name__ == "__main__":
from simpletap import TAPTestRunner
unittest.main(testRunner=TAPTestRunner())
# vim: ai sts=4 et sw=4

159
test/hooks.on-modify.t Executable file
View File

@@ -0,0 +1,159 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
import sys
import os
import unittest
from datetime import datetime
# Ensure python finds the local simpletap module
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from basetest import Task, TestCase
class TestHooksOnModify(TestCase):
def setUp(self):
"""Executed before each test in the class"""
self.t = Task()
self.t.activate_hooks()
def test_onmodify_builtin_accept(self):
"""on-modify-accept - a well-behaved, successful, on-modify hook."""
hookname = 'on-modify-accept'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t(("add", "foo"))
code, out, err = self.t(("1", "modify", "+tag"))
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onmodify_builtin_reject(self):
"""on-modify-reject - a well-behaved, failing, on-modify hook."""
hookname = 'on-modify-reject'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t(("add", "foo"))
code, out, err = self.t.runError(("1", "modify", "+tag"))
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(1)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onmodify_builtin_misbehave2(self):
"""on-modify-misbehave2 - does not emit JSON."""
hookname = 'on-modify-misbehave2'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t(("add", "foo"))
code, out, err = self.t.runError(("1", "modify", "+tag"))
self.assertIn("Hook Error: Expected 1 JSON task(s), found 0", err)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onmodify_builtin_misbehave3(self):
"""on-modify-misbehave3 - emits additional JSON."""
hookname = 'on-modify-misbehave3'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t(("add", "foo"))
code, out, err = self.t.runError(("1", "modify", "+tag"))
self.assertIn("Hook Error: Expected 1 JSON task(s), found 2", err)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onmodify_builtin_misbehave4(self):
"""on-modify-misbehave4 - emits different task JSON."""
hookname = 'on-modify-misbehave4'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t(("add", "foo"))
code, out, err = self.t.runError(("1", "modify", "+tag"))
self.assertIn("Hook Error: JSON must be for the same task:", err)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onmodify_builtin_misbehave5(self):
"""on-modify-misbehave5 - emits syntactically wrong JSON."""
hookname = 'on-modify-misbehave5'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t(("add", "foo"))
code, out, err = self.t.runError(("1", "modify", "+tag"))
self.assertIn("Hook Error: JSON syntax error in: {\"}", err)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
def test_onmodify_builtin_misbehave6(self):
"""on-modify-misbehave6 - emits incomplete JSON."""
hookname = 'on-modify-misbehave6'
self.t.hooks.add_default(hookname, log=True)
code, out, err = self.t(("add", "foo"))
code, out, err = self.t.runError(("1", "modify", "+tag"))
self.assertIn("Hook Error: JSON Object missing 'uuid' attribute.", err)
hook = self.t.hooks[hookname]
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
logs = hook.get_logs()
self.assertEqual(logs["output"]["msgs"][0], "FEEDBACK")
if __name__ == "__main__":
from simpletap import TAPTestRunner
unittest.main(testRunner=TAPTestRunner())
# vim: ai sts=4 et sw=4

View File

@@ -45,6 +45,8 @@ const char *positive_tests[] =
"{\n\"one\"\n:\n1\n}\n",
" { \"one\" : 1 } ",
"{\"name\":123, \"array\":[1,2,3.4], \"object\":{\"m1\":\"v1\", \"m2\":\"v2\"}}",
"{\"name\":\"value\",\"array\":[\"one\",\"two\"],\"object\":{\"name2\":123,\"literal\":false}}",

View File

@@ -1,5 +1,30 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
import sys
import os

View File

@@ -1,5 +1,30 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
import sys
import os

63
test/recur.disabled.t Executable file
View File

@@ -0,0 +1,63 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
################################################################################
##
## Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
import sys
import os
import unittest
# Ensure python finds the local simpletap module
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from basetest import Task, TestCase
class TestRecurrenceDisabled(TestCase):
def setUp(self):
"""Executed before each test in the class"""
self.t = Task()
def test_reccurence_disabled(self):
"""
Test that recurrent tasks are not being generated when recurrence is
disabled.
"""
self.t.config("recurrence", "no")
self.t(("add", "due:today", "recur:daily", "Recurrent task."))
# Trigger GC, expect no match and therefore non-zero code
self.t.runError(("list",))
# Check that no task has been generated.
code, out, err = self.t(("task", "count"))
self.assertEqual("0", out.strip())
if __name__ == "__main__":
from simpletap import TAPTestRunner
unittest.main(testRunner=TAPTestRunner())
# vim: ai sts=4 et sw=4

View File

@@ -28,7 +28,7 @@ else
# because 'date +%s' isn't supported on Solaris.
STARTEPOCH=`perl -e 'print time'`
VRAMSTEG=`which vramsteg`
VRAMSTEG=`which vramsteg 2>/dev/null`
BAR=0
if [ -x "$VRAMSTEG" ]; then
BAR=1

View File

@@ -1,5 +1,30 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
###############################################################################
#
# Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# http://www.opensource.org/licenses/mit-license.php
#
###############################################################################
import sys
import os
@@ -70,6 +95,105 @@ class TestBugNumber(TestCase):
"""Executed once after all tests in the class"""
class TestHooksBugNumber(TestCase):
def setUp(self):
"""Executed before each test in the class"""
self.t = Task()
self.t.activate_hooks()
def test_onmodify_custom(self):
"""Testing a custom made hook"""
hookname = "on-modify-example-raw"
content = """#!/usr/bin/env python
import sys
import json
original_task = sys.stdin.readline()
modified_task = sys.stdin.readline()
task = json.loads(modified_task)
task["description"] = "The hook did its magic"
sys.stdout.write(json.dumps(task, separators=(',', ':')) + '\\n')
sys.exit(0)
"""
self.t.hooks.add(hookname, content)
self.t(("add", "Hello hooks"))
self.t(("1", "mod", "/Hello/Greetings/"))
code, out, err = self.t()
self.assertIn("The hook did its magic", out)
self.t.hooks[hookname].disable()
self.assertFalse(self.t.hooks[hookname].is_active())
self.t(("1", "mod", "/magic/thing/"))
code, out, err = self.t()
self.assertIn("The hook did its thing", out)
def test_onmodify_builtin_with_log(self):
"""Testing a builtin hook and keeping track of its input/output
The builtin hook in found in test/test_hooks
"""
hookname = "on-modify-for-template.py"
self.t.hooks.add_default(hookname, log=True)
self.t(("add", "Hello hooks"))
self.t(("1", "mod", "/Hello/Greetings/"))
code, out, err = self.t()
self.assertIn("This is an example modify hook", out)
hook = self.t.hooks[hookname]
logs = hook.get_logs()
# Hook was called once
hook.assertTriggeredCount(1)
hook.assertExitcode(0)
# Ensure output from hook is valid JSON
# (according to python's JSON parser)
hook.assertValidJSONOutput()
# Some message output from the hook
self.assertEqual(logs["output"]["msgs"][0],
"Hello from the template hook")
# This is what taskwarrior received
self.assertEqual(logs["output"]["json"][0]["description"],
"This is an example modify hook")
def test_onmodify_bad_builtin_with_log(self):
"""Testing a builtin hook and keeping track of its input/output
The builtin hook in found in test/test_hooks
"""
hookname = "on-modify-for-template-badexit.py"
self.t.hooks.add_default(hookname, log=True)
self.t(("add", "Hello hooks"))
self.t.runError(("1", "mod", "/Hello/Greetings/"))
code, out, err = self.t()
self.assertNotIn("This is an example modify hook", out)
hook = self.t.hooks[hookname]
logs = hook.get_logs()
# Hook was called once
hook.assertTriggeredCount(1)
hook.assertExitcode(1)
# Some message output from the hook
self.assertEqual(logs["output"]["msgs"][0],
"Hello from the template hook")
# This is what taskwarrior would have used if hook finished cleanly
self.assertEqual(logs["output"]["json"][0]["description"],
"This is an example modify hook")
class ServerTestBugNumber(ServerTestCase):
@classmethod
def setUpClass(cls):

View File

@@ -1,24 +1,33 @@
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----

View File

@@ -1,32 +1,247 @@
Public Key Info:
Public Key Algorithm: RSA
Key Security Level: High (4096 bits)
modulus:
00:b4:20:a9:9b:6b:47:87:f1:e7:e8:5f:2c:df:d1:
14:3b:46:69:12:8b:39:bd:be:1b:58:1c:59:86:82:
64:ca:22:b1:59:5a:87:96:e1:ce:44:a0:c0:e0:20:
96:e3:08:ff:e5:4c:ec:6b:e6:00:22:21:91:6e:29:
80:c6:d8:3b:c0:0d:1f:f0:5f:aa:67:d7:da:13:09:
f8:81:45:8f:06:1a:5f:75:6c:90:ca:cf:f1:c8:4f:
2b:f7:a7:09:38:99:5e:50:e0:be:b6:16:5b:18:6f:
62:8b:9a:5a:c5:a0:df:7c:6e:4c:df:e2:19:61:c6:
45:48:81:40:f1:5d:17:67:e3:4b:10:59:1d:35:24:
3e:3d:b0:51:fb:55:bd:7e:0e:fb:34:31:44:0d:cd:
7d:7f:50:f6:b7:91:22:e9:d8:03:a1:43:d5:4e:0f:
ae:4f:ca:4c:92:b2:95:dc:b9:e9:86:1b:a9:3d:73:
9f:61:39:5c:07:6e:bc:2a:50:3f:7b:89:03:16:d6:
ff:c0:68:bc:dd:f2:9f:2c:ab:44:14:96:3c:1d:d1:
1e:48:51:48:e5:cf:53:1e:aa:0b:07:22:bc:04:af:
1e:34:b4:e9:17:47:d9:37:1e:eb:69:22:8c:5f:41:
86:fb:3d:18:43:f9:4b:9b:45:0a:63:0e:60:06:b9:
49:0b:1d:ee:03:a9:1d:ea:8a:31:39:bb:ec:9f:a7:
05:c0:11:97:56:1d:3d:6e:a2:43:89:f5:03:d0:f6:
ae:86:0e:f3:dd:89:17:12:ef:0f:25:9f:59:dd:af:
45:8e:e4:b6:5e:0d:06:d1:82:22:08:74:e9:25:12:
79:88:15:fe:f8:f9:5d:e7:84:47:0e:d8:fb:0f:ba:
f4:a6:38:1c:53:50:d9:0b:a9:9d:ed:43:d3:2c:14:
30:8d:ff:e2:8e:5d:6c:a1:83:4e:4d:a7:ac:d0:06:
54:a5:74:cd:64:39:89:11:2d:85:8e:83:4f:02:70:
d5:fa:86:c2:3e:1b:08:5c:cf:d5:d7:aa:11:34:88:
ab:22:0b:85:ad:ef:73:25:98:0f:24:04:2a:e5:fc:
3e:96:bf:11:af:0a:02:38:e2:ca:17:2a:c1:97:1e:
ac:d2:be:a2:c0:14:51:ca:07:2c:31:60:c0:31:b3:
52:a2:54:fd:7b:c7:a5:69:3c:44:38:2e:23:cb:b5:
60:fe:3d:43:8f:c2:60:f4:f2:b4:99:95:11:8b:6f:
2f:6a:9b:bd:76:9a:13:51:ea:62:d2:c4:c7:a6:f4:
86:5d:7f:f2:f4:cc:2b:30:75:e9:32:52:19:fe:da:
50:73:99:27:58:38:08:a8:ec:6f:b1:e5:60:8b:24:
b2:63:fd:
public exponent:
01:00:01:
private exponent:
00:b1:64:91:64:4c:6c:af:c7:5e:da:8c:10:c7:83:
42:e7:75:5a:39:36:ff:f0:8b:6e:a6:81:94:f1:51:
2d:2d:9c:a9:08:14:8d:ff:db:f5:a5:c4:a2:ee:97:
69:c1:c1:b5:31:ff:65:be:85:4e:90:4e:7a:c9:00:
fa:9f:d9:88:4e:85:1e:75:3d:e1:b6:4e:ef:a7:5c:
64:6d:fb:30:f5:93:b7:7b:0f:26:31:76:44:e9:3e:
7f:50:70:22:36:a9:64:27:46:8e:cb:08:8d:42:2d:
3f:e1:b8:6b:b8:c1:cc:16:e1:11:b8:53:15:28:23:
79:4c:04:6f:0e:c4:dd:2e:24:bc:62:27:41:0a:7a:
dd:09:27:c2:35:41:a5:52:a6:32:62:9e:3a:25:55:
c0:48:48:56:11:46:50:19:f7:f0:eb:3a:c0:b1:15:
e4:57:14:26:01:3a:fb:37:db:a7:cd:b9:24:1d:2e:
e7:41:7a:99:d5:80:0a:b3:a9:40:3b:cb:1d:4d:bb:
03:38:5c:6d:d4:2f:02:ef:9c:e3:79:5a:d3:6d:91:
ea:be:d1:be:77:04:8d:19:5f:e1:68:6f:9e:b7:06:
37:3b:35:07:71:34:03:09:b3:db:a7:38:ad:01:8a:
a9:02:78:92:93:2e:ca:74:e9:b3:67:06:5b:2f:83:
fe:17:db:8a:2d:9f:09:08:9f:ce:58:1f:38:72:2c:
36:ac:d1:0c:11:3a:84:4c:40:3d:ad:20:de:39:7a:
2a:b0:d2:e6:a9:c8:5e:78:e7:a0:6d:33:2a:fd:2d:
67:cb:98:ed:b9:01:bb:e6:fc:1e:e2:01:c8:0b:59:
1c:fc:c4:82:2f:d0:95:cf:f4:aa:2d:0d:dc:00:42:
9e:5f:4c:cb:c6:14:ed:9a:4b:d5:95:ad:97:0a:9d:
33:5f:72:ab:ec:e6:4a:4d:61:b4:0c:33:f1:d9:f5:
07:bc:c2:2f:59:4e:8a:59:3e:4d:e2:6d:20:f0:60:
42:44:2a:88:f5:f9:fa:68:97:3d:aa:4b:87:2a:47:
01:9b:be:9d:85:e8:18:3c:ad:bb:5c:0e:6e:d9:88:
bd:23:1e:56:4e:33:13:8a:27:84:6d:b3:d8:bc:be:
70:43:4c:f8:7e:5d:09:5e:35:d3:1e:c7:80:04:1d:
af:81:f2:85:52:3e:3c:98:b2:30:03:29:18:c5:da:
4f:be:58:6d:43:5d:7e:26:bc:9d:23:55:0c:61:cb:
ed:a7:57:c7:50:9a:fc:77:d0:55:c1:a7:a3:ff:3e:
7a:de:9a:0b:42:6c:51:ba:2f:39:a4:01:d1:90:3f:
2c:ff:57:89:65:9e:ad:b8:c2:f6:f5:b8:65:aa:dd:
a2:4a:49:
prime1:
00:dc:e3:cf:71:5a:2f:ef:8a:de:63:6d:c3:3a:1d:
77:12:97:b6:8b:97:4a:c0:5a:21:b0:04:39:67:f8:
c7:8f:21:2a:39:f5:ed:9b:0a:fb:11:3a:68:44:5f:
54:2e:5f:5b:db:db:57:c7:a2:c9:ce:77:32:b2:fd:
b5:8f:12:c6:36:2c:b6:f0:9c:8b:9c:3f:09:b0:4a:
a0:b3:3c:37:2f:46:b3:2f:e6:e6:fe:91:55:c3:aa:
e6:9d:4a:34:4d:6b:c4:c9:24:41:a5:eb:06:4f:11:
39:a1:b0:7a:67:c5:7c:03:f1:e5:de:74:ba:9c:75:
38:46:23:99:87:a6:db:72:9f:93:51:5b:b0:f9:58:
ad:77:ed:a9:48:97:62:8e:f2:43:c3:49:9e:09:70:
08:2f:ec:59:e4:ef:77:c4:2d:72:b3:c7:96:04:47:
e9:9e:1f:ed:eb:09:e8:f5:78:4c:33:ec:37:62:79:
32:ed:7a:4e:9a:05:6e:2c:df:61:40:c1:7e:63:ee:
7c:87:fa:35:87:0a:da:aa:9e:00:74:fa:37:8e:f6:
c4:06:d9:f2:10:e9:19:4d:4a:b0:e3:98:c8:bd:4f:
2d:81:31:07:a6:6a:d7:b0:2e:d3:a8:f9:d7:57:8e:
05:97:39:2e:5e:50:ae:9b:9f:09:98:e6:3c:b8:4c:
6a:bb:
prime2:
00:d0:c2:33:7b:b2:d9:67:66:65:10:ab:67:fe:29:
6e:ec:8b:14:c8:2e:59:ff:7e:57:2e:19:53:20:9e:
1b:bc:3f:d6:62:49:45:dc:3b:bd:83:22:9b:4b:93:
a3:04:9d:9e:6c:54:45:69:56:3f:56:73:4a:76:5c:
50:4d:cb:6d:31:4b:10:80:a6:8c:1f:32:47:14:93:
e1:a2:5f:92:29:28:1c:c4:71:4c:04:e7:3e:5a:3d:
66:2f:64:29:da:9a:50:e3:2f:e6:d5:6d:9c:fa:ad:
7b:db:71:25:63:2a:7e:f8:c2:fd:b2:14:45:7e:1b:
23:81:6f:2d:c5:24:55:46:5f:54:3f:41:38:34:90:
2b:73:6f:e4:53:ba:d8:74:4f:0f:51:d6:37:03:76:
8f:05:77:52:f7:3b:04:38:16:fc:47:ab:e3:33:1b:
dd:c5:2e:69:13:ba:76:bb:a0:58:aa:a3:af:2f:6e:
0c:d0:ae:41:af:b2:2e:cd:84:57:e7:0f:d2:1b:f7:
40:79:d4:e4:3e:74:b2:d9:b8:2e:94:34:76:27:00:
05:6d:ba:d0:1a:46:95:f2:b0:eb:21:33:ca:71:f9:
38:4e:1c:2d:1a:80:84:6e:4b:0d:33:5f:5e:e7:18:
c6:c1:ff:2e:c6:c5:0f:ad:11:21:f5:81:38:00:08:
0c:a7:
coefficient:
00:d4:c5:a5:62:ea:96:09:ba:3d:a8:94:23:81:6e:
f9:94:cd:12:54:8f:24:51:be:ea:cc:4d:b2:77:5e:
75:d5:b6:79:fd:f4:bb:e9:92:94:90:8f:4c:e2:4d:
40:c1:d0:ce:e9:a9:6f:3e:13:f3:b0:48:3d:99:78:
ef:4b:a7:f7:7e:2b:2f:82:63:c6:81:05:9e:86:64:
bc:c8:de:96:d3:54:12:b9:eb:17:65:58:52:8b:ef:
d8:ed:52:fc:e4:19:41:9b:15:81:ac:06:10:6c:e8:
2a:6c:83:44:ee:52:b8:e7:8b:aa:46:33:31:b1:bf:
17:eb:67:a6:9d:99:d2:74:1d:4e:54:dd:50:05:b3:
4e:5f:49:41:9b:29:71:67:19:bd:a1:1e:2b:9c:be:
ea:a0:4f:ae:01:25:aa:0a:f3:00:9a:c9:93:ac:86:
26:f0:14:54:31:c8:1e:b3:3b:e0:a2:fb:f2:df:8a:
35:f5:db:39:08:78:dc:79:fd:97:72:23:ff:b2:f0:
54:ca:17:e2:01:b6:bc:17:4e:60:da:30:16:e0:a1:
02:79:2c:7b:23:00:10:84:66:e8:7c:87:13:64:4f:
17:20:ea:a6:07:ed:ab:d9:7e:1c:1e:d1:f7:20:f4:
a6:a8:d7:46:52:a9:a3:4b:8a:90:e9:83:51:fb:0c:
a7:b7:
exp1:
40:c6:05:04:c6:5f:9c:2c:ea:bd:d9:68:10:58:a7:
9c:2a:24:fb:96:27:f3:73:fa:c0:f4:dc:28:a3:09:
51:ed:75:c6:7d:59:69:c7:b4:8b:fe:c6:a8:96:13:
c7:6d:6b:30:73:87:b0:21:0a:70:a2:ff:99:01:51:
06:5d:89:06:45:e2:77:78:50:70:47:03:fe:50:44:
03:8b:3a:db:80:2d:f7:74:0c:74:c6:05:92:55:41:
3f:0a:8d:7f:ce:84:6c:3f:57:88:e1:fc:7d:9f:1d:
79:4e:98:f6:69:30:ce:af:56:aa:ff:2b:e1:c9:75:
f1:0b:6b:70:1d:fb:e8:06:e7:5a:4a:3b:01:43:81:
ca:d3:44:7a:e6:f9:9d:d5:69:0d:91:3e:e8:a1:93:
a5:09:f7:ea:6c:56:80:8e:9b:3e:20:de:da:ee:83:
91:3c:e7:b5:ae:b8:43:93:3d:35:54:a5:81:18:ed:
58:3a:2f:66:77:05:31:df:67:53:67:ea:3a:3f:ab:
00:ec:dc:59:77:53:5c:08:ed:35:60:52:7e:77:da:
4f:f0:05:4d:f4:2c:d9:3f:77:d3:a5:ef:e7:a3:9a:
42:72:30:3e:1a:c2:99:05:82:47:e0:cb:c6:fe:fc:
a0:b5:c1:e5:22:5f:b1:7e:d7:7f:a0:b4:59:8e:ff:
15:
exp2:
1c:bb:5d:d2:9c:9f:07:78:34:58:c2:fe:a1:20:9a:
bf:bb:b4:b4:88:8c:a2:79:85:0e:37:36:15:bb:2d:
1e:0f:bb:35:f3:d7:c5:a4:03:45:55:32:02:88:e3:
5a:f3:46:a6:aa:42:93:68:e7:76:c2:8f:71:12:eb:
8a:89:81:11:d3:bf:05:b5:e6:7b:34:b4:4b:c4:be:
bd:56:62:0b:80:15:d5:83:b2:6e:ca:54:ee:9e:9c:
bd:d1:0f:61:a2:f3:97:11:34:b8:13:4a:13:c6:64:
d1:a3:14:fe:3d:0f:14:b5:e1:ac:ec:d5:53:b1:f0:
85:14:04:3c:a9:ef:82:ac:04:30:60:8d:97:ce:6f:
ec:87:6d:e9:88:41:da:60:37:3f:90:38:2a:25:db:
31:69:66:e5:d8:1d:90:8a:b9:56:33:f6:25:46:a4:
93:4a:fb:f3:57:2a:98:50:4d:63:ce:fb:81:8f:a7:
fb:e0:bf:f1:aa:98:7e:78:c4:90:6c:c1:02:50:59:
73:6b:35:ae:d2:e8:fa:c6:3a:c2:f3:25:7f:e0:a4:
de:a1:63:9a:fa:34:11:1f:00:7d:a5:5f:2b:a8:9d:
73:42:b5:82:7c:45:bd:d8:5e:8c:bf:87:94:4e:f8:
ac:22:af:11:74:75:df:e0:07:c8:7e:6c:72:b4:a8:
e9:
Public Key ID: EA:D5:94:E7:05:6F:CC:9B:3A:C4:D8:FA:BC:3F:BD:51:39:3F:7E:47
Public key's random art:
+--[ RSA 4096]----+
| |
| |
| . |
| . = .|
| S o+. *o.|
| . o.o+o oE|
| . . .o. o=.|
| . . ....o *|
| . .=+.++|
+-----------------+
-----BEGIN RSA PRIVATE KEY-----
MIIFewIBAAKCATEAxSKzqFLofdSckZfNfyCuUxcllSUEr3Fid1BROO65iP3XZoBf
idVFjUENPto80uwBmMEwtb2wRIsEAJvdELb6jKz2NaHFyRha2+BIufUjMdr8brR8
578+7UCqVrypNaF0Gjqk1h8p29TqRj3CC/qypiQzT+rnRYVLqFWCVC8tWIHgcMCF
5FjmauqU1Bf5vDTU5zRcnY7icr1YqPDJTPU5P/Te6ky0TlNZlkapo1g5RJ9s2LlG
tpNpPmSImg/HYrEbVPZBSlvWopRjwkBlh+8od/B8Ivb1KENqMFh8OnA90m8thN8E
ro51AApKI1jcRK3rrDbe4+nvSnkKwvBwd2s7XlOQ1EGhHix1T2EceOMjymYjkNOt
a9Ie+CJu4xOXU+bJ4QouX4XMa3jBOLrgwZa2dwIDAQABAoIBMHIhMw4CYeNnkfWg
gzJUg1hLkFCWi+3eoPkeoMdrPjMXcvs+2xD7TjcHMFnxE0GxrQL45fByD3i/WuPX
WNbyeH+1Kx3FuLJBzWh6ouA2EdgtRfP5vk6VJCNLStLIitYn0QXWadQlWqCQ1ckw
vAUfcQpK7wIXpylSkIi2aKvAXGpfVscwTtPyvAOuJ9fawZS6qX/piq0VKfjurFuE
AlEUqwhrazxB7Ff7KutAvPkvLyX0MRs5BPhHbBRMAYb+lfxyJdg04tqe2gwhOVoc
8zaOxCIlMfvk0IRbaezfTm9MwXGjHB9yicQj+BtgKtw1YihvaTWebpNUs8UbhUDX
gnzSyRiHVKa6utjfF6oI2DD5nQ7WA50saFD0R0+OCpSTBPYR5NF3LHkBZS1tdni9
B4W5gLECgZkAx89Shl7BrMk4zGWjHiU18tm/bZkxSDmKOxRFjnWaUT0sWWWo/jNj
7zXVzYQoI/tLmismzcJqHMPRxR+jFGGFdHhp9uf04pBqLquCgPHxNHIMpopolFYK
krlLQjDNSqZAdPtCZR5lM1DPC/+YDvhMyRqBBLFmoo0awlcGMTs8pFHVjOpYtTSH
a+1NR+yyd7/tNAbO7PieXFUCgZkA/JLaCtBb3mu076onvZP6twZKUd+5m9G5Gi7v
Pr4W34vtNhtlVdsORgc5NQQB+v1bE2Glp5AtTR4itVUViBxq7Xi3WOwDAmwhpT5H
2L8End887PaZyy3EaSzyvFvc3JqCi3fJ38JMHw07PKmzZcgMY5z69V67R67sLLKy
oot6NJMu7KsdFZUXQtU6NWAYqcJtVqnLdM/wk5sCgZhCF5PmmoVZ0B2Xjq8ME6bq
s5gceqXQjCfrLuNrIf9vi9qVn4jsvLb7s0ULfAGDFWaGkmt9eCd9aFCE8dgRr8hj
JEl4wHVCVB6rXVQHrv/oJymEFRhOqL9KlTwNovLlFGURzOLDa/BxrvMtttfna76K
btfqY3vdnOsTCZIkysFeYStQN1wsZDm/O3lqpyLJdZTStsXPoNVRGQKBmQCO+eZ6
Vqaayyfav9zNQXacou/xVw9v+JGrE8SVJPA3zbMTfQCpUF7NH4uvyjwAw+kJmK44
o7IxRIzrT2Xn7gZNtZc8vCWb+nA1ZYMwXDmV0OhhT9uRm6/ixfIG1I27R6/8mWvq
wcdrkIbm+T23sT3qKdQakGy90HSbfPEIeKJOHr9idB77t7E6QRA5ZNOxTYGtnGKz
Fn8eXwKBmCyXLInD+mMRDdolQNBtcDUzocsyxnvqGlIqxXoQR2vIID4pnDFSjEMg
UBugRNApwvIbfhF1xzAB5k1jNiNY+fPxMHyceK3S5huwEQr9myJDNqhKSpH2Ai62
NTykZTya9d4wsGxGwCERrHuKrAQZuNVvRPS2ahyILT6Wh/9ipC8iCrm4hTHRqTKY
PiYowq/WR28SzglSbdL1
MIIJKQIBAAKCAgEAtCCpm2tHh/Hn6F8s39EUO0ZpEos5vb4bWBxZhoJkyiKxWVqH
luHORKDA4CCW4wj/5Uzsa+YAIiGRbimAxtg7wA0f8F+qZ9faEwn4gUWPBhpfdWyQ
ys/xyE8r96cJOJleUOC+thZbGG9ii5paxaDffG5M3+IZYcZFSIFA8V0XZ+NLEFkd
NSQ+PbBR+1W9fg77NDFEDc19f1D2t5Ei6dgDoUPVTg+uT8pMkrKV3LnphhupPXOf
YTlcB268KlA/e4kDFtb/wGi83fKfLKtEFJY8HdEeSFFI5c9THqoLByK8BK8eNLTp
F0fZNx7raSKMX0GG+z0YQ/lLm0UKYw5gBrlJCx3uA6kd6ooxObvsn6cFwBGXVh09
bqJDifUD0Pauhg7z3YkXEu8PJZ9Z3a9FjuS2Xg0G0YIiCHTpJRJ5iBX++Pld54RH
Dtj7D7r0pjgcU1DZC6md7UPTLBQwjf/ijl1soYNOTaes0AZUpXTNZDmJES2FjoNP
AnDV+obCPhsIXM/V16oRNIirIguFre9zJZgPJAQq5fw+lr8RrwoCOOLKFyrBlx6s
0r6iwBRRygcsMWDAMbNSolT9e8elaTxEOC4jy7Vg/j1Dj8Jg9PK0mZURi28vapu9
dpoTUepi0sTHpvSGXX/y9MwrMHXpMlIZ/tpQc5knWDgIqOxvseVgiySyY/0CAwEA
AQKCAgEAsWSRZExsr8de2owQx4NC53VaOTb/8ItupoGU8VEtLZypCBSN/9v1pcSi
7pdpwcG1Mf9lvoVOkE56yQD6n9mIToUedT3htk7vp1xkbfsw9ZO3ew8mMXZE6T5/
UHAiNqlkJ0aOywiNQi0/4bhruMHMFuERuFMVKCN5TARvDsTdLiS8YidBCnrdCSfC
NUGlUqYyYp46JVXASEhWEUZQGffw6zrAsRXkVxQmATr7N9unzbkkHS7nQXqZ1YAK
s6lAO8sdTbsDOFxt1C8C75zjeVrTbZHqvtG+dwSNGV/haG+etwY3OzUHcTQDCbPb
pzitAYqpAniSky7KdOmzZwZbL4P+F9uKLZ8JCJ/OWB84ciw2rNEMETqETEA9rSDe
OXoqsNLmqcheeOegbTMq/S1ny5jtuQG75vwe4gHIC1kc/MSCL9CVz/SqLQ3cAEKe
X0zLxhTtmkvVla2XCp0zX3Kr7OZKTWG0DDPx2fUHvMIvWU6KWT5N4m0g8GBCRCqI
9fn6aJc9qkuHKkcBm76dhegYPK27XA5u2Yi9Ix5WTjMTiieEbbPYvL5wQ0z4fl0J
XjXTHseABB2vgfKFUj48mLIwAykYxdpPvlhtQ11+JrydI1UMYcvtp1fHUJr8d9BV
waej/z563poLQmxRui85pAHRkD8s/1eJZZ6tuML29bhlqt2iSkkCggEBANzjz3Fa
L++K3mNtwzoddxKXtouXSsBaIbAEOWf4x48hKjn17ZsK+xE6aERfVC5fW9vbV8ei
yc53MrL9tY8SxjYstvCci5w/CbBKoLM8Ny9Gsy/m5v6RVcOq5p1KNE1rxMkkQaXr
Bk8ROaGwemfFfAPx5d50upx1OEYjmYem23Kfk1FbsPlYrXftqUiXYo7yQ8NJnglw
CC/sWeTvd8QtcrPHlgRH6Z4f7esJ6PV4TDPsN2J5Mu16TpoFbizfYUDBfmPufIf6
NYcK2qqeAHT6N472xAbZ8hDpGU1KsOOYyL1PLYExB6Zq17Au06j511eOBZc5Ll5Q
rpufCZjmPLhMarsCggEBANDCM3uy2WdmZRCrZ/4pbuyLFMguWf9+Vy4ZUyCeG7w/
1mJJRdw7vYMim0uTowSdnmxURWlWP1ZzSnZcUE3LbTFLEICmjB8yRxST4aJfkiko
HMRxTATnPlo9Zi9kKdqaUOMv5tVtnPqte9txJWMqfvjC/bIURX4bI4FvLcUkVUZf
VD9BODSQK3Nv5FO62HRPD1HWNwN2jwV3Uvc7BDgW/Eer4zMb3cUuaRO6drugWKqj
ry9uDNCuQa+yLs2EV+cP0hv3QHnU5D50stm4LpQ0dicABW260BpGlfKw6yEzynH5
OE4cLRqAhG5LDTNfXucYxsH/LsbFD60RIfWBOAAIDKcCggEAQMYFBMZfnCzqvdlo
EFinnCok+5Yn83P6wPTcKKMJUe11xn1Zace0i/7GqJYTx21rMHOHsCEKcKL/mQFR
Bl2JBkXid3hQcEcD/lBEA4s624At93QMdMYFklVBPwqNf86EbD9XiOH8fZ8deU6Y
9mkwzq9Wqv8r4cl18QtrcB376AbnWko7AUOBytNEeub5ndVpDZE+6KGTpQn36mxW
gI6bPiDe2u6DkTznta64Q5M9NVSlgRjtWDovZncFMd9nU2fqOj+rAOzcWXdTXAjt
NWBSfnfaT/AFTfQs2T9306Xv56OaQnIwPhrCmQWCR+DLxv78oLXB5SJfsX7Xf6C0
WY7/FQKCAQAcu13SnJ8HeDRYwv6hIJq/u7S0iIyieYUONzYVuy0eD7s189fFpANF
VTICiONa80amqkKTaOd2wo9xEuuKiYER078FteZ7NLRLxL69VmILgBXVg7JuylTu
npy90Q9hovOXETS4E0oTxmTRoxT+PQ8UteGs7NVTsfCFFAQ8qe+CrAQwYI2Xzm/s
h23piEHaYDc/kDgqJdsxaWbl2B2QirlWM/YlRqSTSvvzVyqYUE1jzvuBj6f74L/x
qph+eMSQbMECUFlzazWu0uj6xjrC8yV/4KTeoWOa+jQRHwB9pV8rqJ1zQrWCfEW9
2F6Mv4eUTvisIq8RdHXf4AfIfmxytKjpAoIBAQDUxaVi6pYJuj2olCOBbvmUzRJU
jyRRvurMTbJ3XnXVtnn99LvpkpSQj0ziTUDB0M7pqW8+E/OwSD2ZeO9Lp/d+Ky+C
Y8aBBZ6GZLzI3pbTVBK56xdlWFKL79jtUvzkGUGbFYGsBhBs6Cpsg0TuUrjni6pG
MzGxvxfrZ6admdJ0HU5U3VAFs05fSUGbKXFnGb2hHiucvuqgT64BJaoK8wCayZOs
hibwFFQxyB6zO+Ci+/LfijX12zkIeNx5/ZdyI/+y8FTKF+IBtrwXTmDaMBbgoQJ5
LHsjABCEZuh8hxNkTxcg6qYH7avZfhwe0fcg9Kao10ZSqaNLipDpg1H7DKe3
-----END RSA PRIVATE KEY-----

View File

@@ -1,24 +1,33 @@
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----

View File

@@ -1,32 +1,247 @@
Public Key Info:
Public Key Algorithm: RSA
Key Security Level: High (4096 bits)
modulus:
00:f0:6e:0c:8d:82:c9:98:2d:73:18:4a:ed:8a:d1:
1f:68:4c:d3:15:e0:aa:84:88:5c:ad:7b:7d:55:2d:
91:75:d9:ed:38:f2:09:31:89:30:5d:42:f0:5d:b5:
a4:b1:bf:c7:d0:8a:33:e9:ce:3b:28:5d:0d:43:b4:
cc:7f:7a:eb:36:4f:17:01:39:13:b5:c1:9f:5c:be:
a6:bc:64:4e:ea:64:1d:40:10:c2:db:62:53:cf:0a:
61:a1:ea:b2:a7:b4:0e:18:d1:f0:0d:61:62:a5:0e:
4e:64:a8:57:21:e0:ef:78:f3:65:43:ed:4f:47:5b:
fd:47:fc:81:dc:81:d4:1a:75:13:0b:30:57:90:af:
73:b0:47:1d:68:57:80:04:b9:95:5a:83:d5:df:a2:
55:25:44:f1:e2:76:93:49:8b:a5:00:04:bc:b8:56:
91:ee:bc:b8:67:29:cd:cf:d9:28:d6:25:7e:fd:93:
eb:e6:e6:7c:af:56:f6:ae:25:64:fa:d6:d1:81:8c:
cf:6e:47:96:9b:9c:b6:33:44:74:e1:80:65:02:39:
1f:a5:2c:26:c4:9f:fc:32:c1:bc:b3:d2:c2:f9:ab:
b2:4e:6a:1f:be:58:84:4b:70:7e:31:a0:e4:e3:27:
4a:34:0f:62:ea:ee:0d:4f:5b:24:83:e1:f7:d1:9d:
92:e3:6e:38:80:c4:5d:77:28:94:2f:f3:a9:f2:59:
ba:39:6b:7d:d4:1a:c6:74:34:f6:42:a5:3b:a9:ae:
d7:00:0d:c8:5e:a6:ad:c9:69:0c:86:96:d9:b9:8d:
63:80:35:f5:35:05:51:05:a4:04:cd:d9:54:a1:cb:
fd:0a:4d:6f:d9:a2:b7:2b:e3:f6:18:ac:5e:de:37:
e7:3d:ff:87:89:23:d6:3a:ac:94:ae:09:6b:92:5f:
c9:7d:68:38:1f:89:f8:fe:e3:bc:2b:5f:c6:30:6c:
ae:89:98:88:3f:0f:49:83:d6:1f:18:c6:ba:95:e7:
4c:34:ef:3f:e5:ef:61:8f:02:18:fd:6f:59:8b:5a:
d3:e7:5f:1e:45:a4:e3:82:86:0f:c4:dc:dd:5e:4b:
67:62:70:c3:bb:61:64:78:19:e3:1d:7e:a7:7e:d2:
7f:b3:d2:06:57:05:1c:ee:f6:d5:a6:f8:71:71:71:
ae:43:72:65:63:55:1f:df:97:53:ea:78:6a:45:e3:
df:08:74:8a:f9:a2:4a:7f:60:30:bc:6b:d0:97:77:
d5:4a:b9:28:89:b9:09:36:bc:e5:4f:3f:21:b9:5e:
a3:c1:4d:95:02:19:3b:f8:64:41:8e:8c:3c:d5:80:
09:f0:2b:87:e5:fb:5d:b2:e6:52:48:11:36:e4:12:
c8:41:db:
public exponent:
01:00:01:
private exponent:
20:05:07:f6:f0:ac:18:70:35:1b:cf:1e:c3:96:06:
30:c5:ca:76:e7:6c:ce:29:ba:ed:ff:bc:39:53:d0:
43:79:ba:76:13:05:d6:6b:06:dd:e4:c4:4e:36:9a:
b0:71:72:12:fb:d4:de:16:71:1a:77:0a:05:32:56:
e9:c3:5c:ff:e6:98:91:b9:20:3e:dc:82:74:bf:8f:
45:17:b6:ca:d0:7a:74:c5:c6:ad:a0:f2:b8:68:11:
88:96:d1:24:34:7e:8c:1f:a9:70:9e:bf:9c:6b:c3:
54:f4:76:66:34:a6:af:dc:fa:67:89:7b:3d:6d:aa:
d5:a3:73:48:02:a2:54:c3:b2:bd:92:ba:96:b2:47:
1d:fa:da:97:c2:cf:71:d1:a5:6a:60:94:63:2c:23:
5a:cb:4b:cb:75:2f:38:64:38:9d:40:04:42:28:85:
56:de:1b:50:e1:e8:5e:fd:f4:ca:50:44:11:51:59:
e1:6e:60:bc:99:40:51:00:f9:e9:ce:22:28:b6:b0:
09:67:cf:4e:fa:17:e9:cb:fc:24:ec:0c:87:b7:43:
d7:a3:81:a6:3e:08:40:5a:3c:df:55:fa:51:75:e4:
51:3d:20:ed:20:ac:bd:bf:45:8f:26:76:55:2a:2e:
5f:b0:8c:e3:ec:c0:29:09:62:5b:8e:04:8b:0e:09:
39:ae:a6:e8:6f:2f:25:34:01:e7:53:f2:fb:50:08:
23:3f:5f:45:e6:71:68:27:55:00:59:3e:37:53:df:
3b:bd:a3:1a:3b:a1:06:f0:ff:90:b5:30:cf:14:a8:
59:86:c4:25:df:38:b1:29:ff:47:53:94:6b:b1:62:
03:c8:23:8e:97:da:b2:6b:c9:15:15:f0:5d:50:c2:
d9:01:ef:52:65:2f:c4:c2:bc:3a:e6:32:a0:54:54:
f9:0c:96:da:15:5a:b3:4d:77:04:7d:73:11:48:06:
e9:8f:9f:6c:61:0e:64:21:6e:14:2d:23:70:ad:c1:
86:2b:23:12:08:7f:09:1d:07:24:9b:84:9c:ef:e6:
bd:ae:77:6b:01:2c:60:bf:0a:56:2e:29:07:4e:c5:
24:72:71:ba:eb:82:9e:7f:6c:0e:dd:a8:2d:f9:78:
f6:d6:5c:05:39:4b:e2:ba:3f:c8:32:3b:4d:3b:16:
dc:b2:30:f2:12:99:87:6c:e7:c5:66:2c:74:34:d0:
30:81:8e:e9:6f:df:9f:9c:d6:2f:fe:82:e5:d9:d9:
3d:0b:2f:9b:85:d6:2b:0c:2f:98:6a:e1:b3:34:d0:
3b:84:ce:5a:f9:04:81:29:f4:02:97:5b:9a:12:de:
4b:64:48:94:f6:ee:3f:be:55:77:73:18:c4:f3:0e:
8b:c1:
prime1:
00:f9:d7:c4:3c:dc:91:90:0c:08:e2:67:e1:9b:4e:
6d:8a:3f:6a:dc:84:67:b7:30:97:e6:40:30:42:56:
06:23:80:a3:60:a0:34:11:d7:e0:5c:80:1e:5c:7c:
80:9d:32:bd:3f:0e:df:dc:4f:a1:0b:a9:be:48:40:
2b:09:35:d2:37:58:89:90:7c:7c:31:03:71:9e:84:
f9:be:b0:a2:0e:f7:d8:4f:4b:6c:37:67:c0:a7:80:
9b:7b:c1:bf:be:89:55:46:ff:d7:88:7a:3b:d1:63:
80:c3:04:1c:05:8c:dc:68:19:32:e7:db:4d:dd:0f:
59:41:5b:2d:8d:a8:f5:dc:90:c8:31:45:05:b3:d7:
63:ea:ba:fe:9b:9e:02:2a:d3:3e:c0:40:35:99:f9:
cb:c4:f9:42:c2:bc:73:f7:2c:f7:78:65:92:9a:ab:
e4:be:85:59:f9:e7:84:5e:c3:a6:36:98:70:d7:6a:
97:21:17:4e:ee:cd:29:23:02:68:fa:4c:8c:7f:22:
f8:0e:8b:18:09:3b:17:6f:6f:36:f7:2a:50:dd:15:
b6:13:e7:e2:7d:2e:13:b8:87:74:af:c3:12:2d:aa:
92:d7:64:7a:5d:40:d0:08:f5:a7:c8:91:c3:b4:59:
e7:5b:63:dc:fd:11:9f:24:d2:4c:93:37:d8:88:f6:
14:b7:
prime2:
00:f6:5a:e5:a6:05:1b:f1:05:1f:27:03:33:9e:2c:
58:72:a6:ac:a7:66:75:d3:80:74:98:12:b0:09:0e:
3e:c2:30:81:5a:5f:2a:48:3b:67:70:0a:2b:92:c4:
52:ec:20:a8:f3:96:5f:e4:ca:7d:34:19:31:b7:6d:
68:f0:f2:43:9f:a8:7b:2f:3c:93:d4:20:e6:d1:39:
9e:eb:e3:36:42:8f:b8:68:0d:05:ad:f3:ab:f8:e4:
4b:b4:26:66:01:bb:e7:51:d0:af:38:ec:41:b6:f9:
cc:ce:ae:b9:aa:db:b5:bf:9d:8c:d8:50:35:ef:e4:
84:04:e9:42:f4:1c:0a:98:bc:7c:7f:46:b9:24:6a:
2a:22:d0:d0:85:a7:0e:eb:ec:5b:28:22:83:45:d4:
45:06:a7:1d:e4:6b:2f:04:21:e6:da:a4:22:23:71:
b7:a1:9d:be:b4:d3:73:54:a7:2a:b3:32:6b:2d:f8:
d3:b5:19:64:c7:98:94:01:d5:8b:68:79:66:c2:b2:
fd:cf:5f:87:87:6e:37:f1:0e:1b:f6:17:03:5b:2f:
8a:30:f7:3f:20:1e:38:4d:07:9a:6d:c0:ca:aa:a7:
83:b9:b6:85:2b:ec:4f:96:82:ac:f1:3e:26:b9:5f:
d6:0d:bc:84:e6:bc:fb:37:d8:0a:42:27:a3:3e:29:
7f:fd:
coefficient:
00:88:e9:77:eb:66:73:61:1e:dd:97:27:e8:ef:9b:
16:75:8c:a6:74:6a:fd:af:92:0b:cb:80:f3:08:c9:
48:0a:eb:38:42:64:42:f7:e3:49:d6:a4:67:3e:67:
8f:84:5f:7d:3a:7b:fa:57:22:83:57:f4:b9:fc:88:
03:01:d1:df:52:33:b0:e8:08:cb:02:ed:c1:5e:04:
b4:9c:12:0d:c3:37:0a:db:b8:5f:93:32:58:4f:0a:
d4:32:dd:89:7e:f3:41:3b:42:31:da:31:1e:41:eb:
df:7a:10:af:e9:be:47:cd:59:02:73:b4:ea:be:44:
6a:b6:27:08:25:be:d0:52:48:09:01:16:10:01:d2:
7c:d6:88:50:3c:94:31:4c:a7:dd:31:a8:b9:4d:b4:
de:08:72:8a:42:b2:93:c3:a1:b7:e7:63:0c:b4:7a:
8e:9d:dd:dd:cb:e7:01:3b:1f:fc:d3:10:38:9c:ca:
db:42:f2:87:6b:e6:71:6e:c1:41:bc:0f:ef:87:a9:
78:98:19:ea:71:8d:c8:2a:54:64:86:d5:34:6a:c7:
41:66:4d:18:1e:b1:ec:3e:b0:31:79:6c:20:24:d8:
e1:7b:3d:a4:3e:b1:81:50:18:4a:ae:7f:6d:a7:c2:
98:0e:be:ca:99:27:b8:79:44:46:89:07:94:b5:e0:
92:f5:
exp1:
5a:58:a7:12:e7:24:10:35:f9:e4:7d:72:3c:ea:20:
7c:74:b0:5c:eb:d8:61:8b:45:f2:b2:55:81:37:80:
62:c8:3a:cc:4f:42:65:0d:6e:89:a4:9e:d6:0d:03:
a9:54:d6:d0:7d:bd:7f:fa:9f:9f:0b:13:cf:26:bd:
29:48:78:2b:21:6e:25:7c:8e:a0:5e:c1:70:74:5f:
a7:90:52:1a:1d:8f:f0:24:b3:0e:5a:0b:75:c6:f0:
f2:d0:be:58:d5:6a:ac:11:0a:4c:c4:fe:ea:9a:13:
ce:f1:a6:7c:6f:52:a8:a6:2c:1d:61:d5:87:51:ec:
91:d0:b9:e9:57:27:09:4a:a5:ca:03:4e:ab:99:00:
fd:b9:ff:26:20:fe:61:a4:20:35:ff:42:22:db:0c:
cf:be:df:60:72:2b:71:3f:a6:81:b8:21:4a:a3:4c:
45:85:0d:63:6c:50:16:c0:a3:53:6c:ce:a9:67:57:
1d:55:43:6a:e1:6f:b5:c4:42:57:f8:e4:79:79:18:
c0:91:95:f0:88:98:d1:e8:22:b5:40:6d:1b:75:a5:
71:0f:52:7c:b7:f3:d3:af:d0:07:5b:00:30:d9:54:
de:64:ec:46:da:90:20:1d:4d:3e:1f:4f:e8:e0:f5:
e3:bb:7a:07:5d:9e:f1:fe:c6:48:1a:52:c8:3c:4c:
83:
exp2:
00:a2:ae:d7:e4:50:8f:69:14:26:c4:4e:7a:f1:c2:
fb:0d:8b:3f:8c:b4:3e:ae:d2:af:2c:15:7b:26:1f:
c0:15:e0:8e:2b:2d:98:f0:66:3a:22:64:b4:ed:90:
b3:e9:b6:8f:9b:7b:fc:0b:4c:24:6e:19:0d:19:c2:
28:80:a4:9b:3f:7d:b5:0b:36:87:b0:97:a1:95:1c:
e5:f3:83:f1:ba:f6:c9:a1:fc:a7:79:7d:e6:5c:cf:
f5:a6:1b:8d:55:2b:ce:cf:da:6d:3b:bb:d8:91:8a:
58:53:4c:52:d3:9f:8f:5f:e0:1c:b8:64:0f:9a:41:
5f:65:16:dc:dd:f2:fe:94:6a:34:14:a0:91:76:98:
9d:06:ac:ba:66:e8:31:14:5e:da:bb:a0:3e:ec:08:
07:25:23:8a:bb:e2:84:03:c8:97:78:5e:d7:24:6c:
de:04:dd:f4:a7:d8:d2:da:5e:21:f6:80:1f:03:16:
c4:47:5d:63:8d:e3:16:2a:37:e8:7d:af:5d:ca:c9:
04:8e:c2:d8:ee:f8:3b:fc:f5:82:45:7a:60:ff:00:
5f:d3:82:12:af:95:05:3c:e8:fe:73:e7:d2:6d:eb:
7d:33:50:72:94:d4:d2:a1:e3:dd:6c:d9:cc:1c:b2:
4a:da:7b:59:90:3d:cf:ef:ca:bc:cb:7e:fd:6f:f9:
5b:c9:
Public Key ID: 74:D8:EC:2A:C5:84:BC:65:11:5B:BF:61:1A:6C:B7:BD:68:5A:4C:6F
Public key's random art:
+--[ RSA 4096]----+
| o.. |
| . . O . |
| o B B = |
| B + = = |
| . S o + . |
| . . o o . |
| . . = E |
| . + . |
| . |
+-----------------+
-----BEGIN RSA PRIVATE KEY-----
MIIFfQIBAAKCATEAvoi36do6MWZbBiRAxcuKtg+sa+8ammOXVFLIGleecwR1a14n
xCyLZyMXnd7VbhZIkweXlo/Ts+wqle8IejsXVspffyBGaRLCkHP0KyKvvTx4wvuX
4cGMjd05ZwpApPlWIJ73/QZokgAShVtwkSF3zOUsqB/Aa82KqmBv9iSGM+8lj5N2
ZqO+pnG2tAJOB6m2N0u0+NzZD7B47wfreQ2UAiJS4SLKlW5jG79YaeMQ/jDfgNn3
kMOVraPbVAZhRd9MVafT+mV70W0lVCvwcGNORWOi+J33Tz0Kkh2X/0SfmSb3uOjo
WQsdXqCfLQXjjb3UbfOpbKBQhtrL0eJ/ZDl/wHjRC3S6dyEY0tnvqoR/HsCwVkbM
vQUvxU0NISPjdvH0CHj7NNhiMimuyNdP8y7tvwIDAQABAoIBMQC3Nqp68YChvgXr
KvMp/6Z7mSi4hSw0p21+BTgrtHNFsFTkkJ52kkreOy1bfkoHWOAimdtXSShOS9tL
X5XsS2QO7fzUNoneQc7brtRsoicRNdP90oZi7IWmaMLo5T/yI1yBc7uNEBt/wRgO
KjZdKCyV1w2RAS87umMLoD4pF0gBKoYYwLjKy8vpK1F/WPQFhFkWZPM5lswWu3VF
tvIwBk91HoL6eueIZ3vO8z7+T1pQvComeELG5sZ3HALte39YfTEwQaTRBSGyPolf
sB2098b1bhzsXLJAjkdmD2SKP0g9eVBFDn/t+2XXsEtFiqq5JB2INdu8pseOHuBR
V1w7FL7jPT2z/lx3BflhVWLijKVfU5F5/DnDMmiCFY0/aIZxZJU+PO/zyIWHYpPb
mxBTdB7xAoGZAPPzAiWMVFVs10p8oMwXnHwdMVLZsAd49qXSTKgTCdyF30BxVVCV
drg4aGyoUT7T676IgG1aqF7YmpFkMjTaDIiYIRTLhsQrRHmsZpKKsrYclU77m80Q
5V6S4IbW0rdM0pZ2PjtmZMwk2NkpH1Yl/ANNd2tkM9f9e9ZMRAiKxkyC8cj0pLRm
Rnuu5e9RiUsVq/g92/2OyE83AoGZAMfyOCuGs6o+YpTTQPgnMShViz9Xu4GqaVcH
dg6dtaOiSLW053ShCOKTOpCtBW++IQzZE9iAd+dyIIQCAZXwOKDdVxH3x3uUvwaY
rbdX2gsObbB4K9UgZ774n0FNTT8OlI7XOKAY+qW5H+GV22iDANrp2HvqL0s4pcUj
+2oqpgWSOG5XHnlcOUsgGtKn4U9wZpwmTHtuYUm5AoGZAOgTN+iWrqgyCkKPe72N
fM5KS2EhPShuuF6aSVjf+9Kx/OhmqR4ngrDQ4O711YzquOwjGeJui5znfWxxoJ3N
Dn1oFJie4s9+xf7mtxIUJt2/JX/lN5wHxd2J51X/yqicyf5f8OY7jAr1xuIZD/m5
2x0dNk2RHw0M1iHRnituMiYZsIFX09TXM5h4k8Bvlig7LsXTu5RloERTAoGYMmQ8
fdPMEhr483Mui5wtCnJMzuEIk8xkEhgCS/q5XY037/uw3bb8K0ZZ2z6QATKU180Z
8FWoYn9j7IEo7yYEz54ZJKEi+GVro3j1RyDwBhtTz8bpnSQ2oW29jVKzJjJBhKJ/
VfIVsZCvlUYUbwmdPI71DX3e/4E+XioMYhxvjTXl3QE93+47TlDAEdlXspKseaw5
WF5gTJkCgZkA8Hi24HicbqzxpQ8WeiG8Vl/Qkm7kANqDTwO6ZhcuWrO5rTqFlmMl
Q5IpyYVVmSRo+Sd+2MRXskie6/tmaAgXlNUIBL0cDupIwk66z3loiKUSaocTpeia
Pt8HJcpTk/JWlqJ78mfVS9KKaBdC/TSVs9N9EO/KHAJDl8UsbLh2q3T8Wjc2+ROZ
XnwlBhqrbULWIEcvGWW4FzQ=
MIIJKQIBAAKCAgEA8G4MjYLJmC1zGErtitEfaEzTFeCqhIhcrXt9VS2RddntOPIJ
MYkwXULwXbWksb/H0Ioz6c47KF0NQ7TMf3rrNk8XATkTtcGfXL6mvGRO6mQdQBDC
22JTzwphoeqyp7QOGNHwDWFipQ5OZKhXIeDvePNlQ+1PR1v9R/yB3IHUGnUTCzBX
kK9zsEcdaFeABLmVWoPV36JVJUTx4naTSYulAAS8uFaR7ry4ZynNz9ko1iV+/ZPr
5uZ8r1b2riVk+tbRgYzPbkeWm5y2M0R04YBlAjkfpSwmxJ/8MsG8s9LC+auyTmof
vliES3B+MaDk4ydKNA9i6u4NT1skg+H30Z2S4244gMRddyiUL/Op8lm6OWt91BrG
dDT2QqU7qa7XAA3IXqatyWkMhpbZuY1jgDX1NQVRBaQEzdlUocv9Ck1v2aK3K+P2
GKxe3jfnPf+HiSPWOqyUrglrkl/JfWg4H4n4/uO8K1/GMGyuiZiIPw9Jg9YfGMa6
ledMNO8/5e9hjwIY/W9Zi1rT518eRaTjgoYPxNzdXktnYnDDu2FkeBnjHX6nftJ/
s9IGVwUc7vbVpvhxcXGuQ3JlY1Uf35dT6nhqRePfCHSK+aJKf2AwvGvQl3fVSrko
ibkJNrzlTz8huV6jwU2VAhk7+GRBjow81YAJ8CuH5ftdsuZSSBE25BLIQdsCAwEA
AQKCAgAgBQf28KwYcDUbzx7DlgYwxcp252zOKbrt/7w5U9BDebp2EwXWawbd5MRO
NpqwcXIS+9TeFnEadwoFMlbpw1z/5piRuSA+3IJ0v49FF7bK0Hp0xcatoPK4aBGI
ltEkNH6MH6lwnr+ca8NU9HZmNKav3PpniXs9barVo3NIAqJUw7K9krqWskcd+tqX
ws9x0aVqYJRjLCNay0vLdS84ZDidQARCKIVW3htQ4ehe/fTKUEQRUVnhbmC8mUBR
APnpziIotrAJZ89O+hfpy/wk7AyHt0PXo4GmPghAWjzfVfpRdeRRPSDtIKy9v0WP
JnZVKi5fsIzj7MApCWJbjgSLDgk5rqboby8lNAHnU/L7UAgjP19F5nFoJ1UAWT43
U987vaMaO6EG8P+QtTDPFKhZhsQl3zixKf9HU5RrsWIDyCOOl9qya8kVFfBdUMLZ
Ae9SZS/Ewrw65jKgVFT5DJbaFVqzTXcEfXMRSAbpj59sYQ5kIW4ULSNwrcGGKyMS
CH8JHQckm4Sc7+a9rndrASxgvwpWLikHTsUkcnG664Kef2wO3agt+Xj21lwFOUvi
uj/IMjtNOxbcsjDyEpmHbOfFZix0NNAwgY7pb9+fnNYv/oLl2dk9Cy+bhdYrDC+Y
auGzNNA7hM5a+QSBKfQCl1uaEt5LZEiU9u4/vlV3cxjE8w6LwQKCAQEA+dfEPNyR
kAwI4mfhm05tij9q3IRntzCX5kAwQlYGI4CjYKA0EdfgXIAeXHyAnTK9Pw7f3E+h
C6m+SEArCTXSN1iJkHx8MQNxnoT5vrCiDvfYT0tsN2fAp4Cbe8G/volVRv/XiHo7
0WOAwwQcBYzcaBky59tN3Q9ZQVstjaj13JDIMUUFs9dj6rr+m54CKtM+wEA1mfnL
xPlCwrxz9yz3eGWSmqvkvoVZ+eeEXsOmNphw12qXIRdO7s0pIwJo+kyMfyL4DosY
CTsXb2829ypQ3RW2E+fifS4TuId0r8MSLaqS12R6XUDQCPWnyJHDtFnnW2Pc/RGf
JNJMkzfYiPYUtwKCAQEA9lrlpgUb8QUfJwMznixYcqasp2Z104B0mBKwCQ4+wjCB
Wl8qSDtncAorksRS7CCo85Zf5Mp9NBkxt21o8PJDn6h7LzyT1CDm0Tme6+M2Qo+4
aA0FrfOr+ORLtCZmAbvnUdCvOOxBtvnMzq65qtu1v52M2FA17+SEBOlC9BwKmLx8
f0a5JGoqItDQhacO6+xbKCKDRdRFBqcd5GsvBCHm2qQiI3G3oZ2+tNNzVKcqszJr
LfjTtRlkx5iUAdWLaHlmwrL9z1+Hh2438Q4b9hcDWy+KMPc/IB44TQeabcDKqqeD
ubaFK+xPloKs8T4muV/WDbyE5rz7N9gKQiejPil//QKCAQBaWKcS5yQQNfnkfXI8
6iB8dLBc69hhi0XyslWBN4BiyDrMT0JlDW6JpJ7WDQOpVNbQfb1/+p+fCxPPJr0p
SHgrIW4lfI6gXsFwdF+nkFIaHY/wJLMOWgt1xvDy0L5Y1WqsEQpMxP7qmhPO8aZ8
b1KopiwdYdWHUeyR0LnpVycJSqXKA06rmQD9uf8mIP5hpCA1/0Ii2wzPvt9gcitx
P6aBuCFKo0xFhQ1jbFAWwKNTbM6pZ1cdVUNq4W+1xEJX+OR5eRjAkZXwiJjR6CK1
QG0bdaVxD1J8t/PTr9AHWwAw2VTeZOxG2pAgHU0+H0/o4PXju3oHXZ7x/sZIGlLI
PEyDAoIBAQCirtfkUI9pFCbETnrxwvsNiz+MtD6u0q8sFXsmH8AV4I4rLZjwZjoi
ZLTtkLPpto+be/wLTCRuGQ0ZwiiApJs/fbULNoewl6GVHOXzg/G69smh/Kd5feZc
z/WmG41VK87P2m07u9iRilhTTFLTn49f4By4ZA+aQV9lFtzd8v6UajQUoJF2mJ0G
rLpm6DEUXtq7oD7sCAclI4q74oQDyJd4XtckbN4E3fSn2NLaXiH2gB8DFsRHXWON
4xYqN+h9r13KyQSOwtju+Dv89YJFemD/AF/TghKvlQU86P5z59Jt630zUHKU1NKh
491s2cwcskrae1mQPc/vyrzLfv1v+VvJAoIBAQCI6XfrZnNhHt2XJ+jvmxZ1jKZ0
av2vkgvLgPMIyUgK6zhCZEL340nWpGc+Z4+EX306e/pXIoNX9Ln8iAMB0d9SM7Do
CMsC7cFeBLScEg3DNwrbuF+TMlhPCtQy3Yl+80E7QjHaMR5B6996EK/pvkfNWQJz
tOq+RGq2JwglvtBSSAkBFhAB0nzWiFA8lDFMp90xqLlNtN4IcopCspPDobfnYwy0
eo6d3d3L5wE7H/zTEDicyttC8odr5nFuwUG8D++HqXiYGepxjcgqVGSG1TRqx0Fm
TRgesew+sDF5bCAk2OF7PaQ+sYFQGEquf22nwpgOvsqZJ7h5REaJB5S14JL1
-----END RSA PRIVATE KEY-----

View File

@@ -1,25 +1,34 @@
-----BEGIN CERTIFICATE-----
MIIEMzCCAuugAwIBAgIEU7iVFjANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJT
RTEeMBwGA1UECgwVR8O2dGVib3JnIEJpdCBGYWN0b3J5MRIwEAYDVQQHDAlHw7Z0
ZWJvcmcxGjAYBgNVBAgMEVbDpHN0cmEgR8O2dGFsYW5kMR4wHAYDVQQDDBVHw7Z0
ZWJvcmcgQml0IEZhY3RvcnkwHhcNMTQwNzA2MDAxNTE4WhcNMTUwNzA2MDAxNTE4
WjBAMR4wHAYDVQQKDBVHw7Z0ZWJvcmcgQml0IEZhY3RvcnkxHjAcBgNVBAMMFUfD
tnRlYm9yZyBCaXQgRmFjdG9yeTCCAVIwDQYJKoZIhvcNAQEBBQADggE/ADCCAToC
ggExAKsv5Nau53tmC8n3UH+FmraZPNr7glF0sDe0ofGz7YL2omKgE4QXIumiJ6ab
5jXWaFwubhiy6WEdSPtOkdB1VelNzfm0jPSXsDJhowfaUBP5F4iw7UUfapJ3feXf
Y5ieJj+Dd1jsOIWwnOFvl+qvMC+KtjH4v+4nTRJ0QdI5/EheEJECmtxAS4XtmNJV
qQ2lRquGOxHz4tAzH1dl2WDKZm6/Jn9gKxH66iGIKMrkbRuaVTbZI838SK2GJRLp
3FAIEps87kOitfZkQeMh15nsTW1e+HCitsyDiCY+cW7D5uTSn07c4pMYo0VXU5BH
Gwj+gaNYZPP0G088g9hRg4c1oufYGhkO0G4P583+/7Nc2eZWOve3o/tJwEfGT8pk
FTgAOc+ZBSQUgqyoswiWyTsZhqcCAwEAAaOBlzCBlDAMBgNVHRMBAf8EAjAAMB4G
A1UdEQQXMBWCDXRhc2t0b29scy5vcmeHBGFrjWswEwYDVR0lBAwwCgYIKwYBBQUH
AwEwDwYDVR0PAQH/BAUDAwegADAdBgNVHQ4EFgQURXMgYOwqEdOjh4T6gXCNph1i
W+kwHwYDVR0jBBgwFoAUV6hrNd+llELYw9FhchM/2QvO37UwDQYJKoZIhvcNAQEL
BQADggExAG/0Fr2FYjtxnQNqQukODgLhE0nuR78G4aLbO3UYEP7vAaclUKlF2IsE
aTSnvOqIF4pwrrA08D0fu5ZlHwJ//JQhxyrJAHmmYol3zX48iVAau8KD4FvEsLV7
d2SERbtl02yZ/e2J50pyEikwgke4I+V/Wjphg28tOZVRPFZy+N8J+j7Dn/faTlJF
mp121KuyR098YV6EDNSnUMDsaVCO8y/OfQXewDMJdVTeYapH91S6RU5b1q16mIHQ
bFBJhWcOXTJdCpxq8nSAXYOIk5EKFIJqrlzA6ZcoiOtva5m1isSjyhQ/fotLAduX
YJ1qbTghXCTWuUxX+GjUDqnNK3pHfTCUYJALaT95IunyB/YJGGgFmUoc3hqP+rcQ
2EyLepPryOLhqXsETgYnMKyBhXRhzAM=
MIIF2zCCA8OgAwIBAgIMVL2QsQ435IB4NhCcMA0GCSqGSIb3DQEBCwUAMH0xHjAc
BgNVBAMMFUfDtnRlYm9yZyBCaXQgRmFjdG9yeTEeMBwGA1UECgwVR8O2dGVib3Jn
IEJpdCBGYWN0b3J5MRIwEAYDVQQHDAlHw7Z0ZWJvcmcxGjAYBgNVBAgMEVbDpHN0
cmEgR8O2dGFsYW5kMQswCQYDVQQGEwJTRTAiGA8yMDE1MDExOTIzMTgwOVoYDzIw
MTYwMTE5MjMxODA5WjBAMR4wHAYDVQQDDBVHw7Z0ZWJvcmcgQml0IEZhY3Rvcnkx
HjAcBgNVBAoMFUfDtnRlYm9yZyBCaXQgRmFjdG9yeTCCAiIwDQYJKoZIhvcNAQEB
BQADggIPADCCAgoCggIBAMi1jICEBV1O52s0SulKT9UvOT7vUGrQqSFPVkwMHcWw
Mxmt/oRAwPijwpdLnrBt3PYU2sZD8gfmbBluqr72GOUViCBQemwj0RV677Z4zjyv
Doj54wmzAL4PkWgCPZ2T6h3pGZEdq9qStXvMJqk0irIsQ44kaL3y1Qm4ayhzt0QT
+BTfQIkUIV/AQVM5XXZ1vo1ElL+m797LOiyPJu1gDQZDzi92oHJUxr4kvPrtIod6
fwqPKSjCcWPT6uSha+vw3JMW4oiYHwANJnCpV6dDFev6mm+d5Uz25a+v+awzd1qN
BrX3yg04jY8GKdYv43+JGVzvhfkkqNAIp71N4jAoTBmpmsFDzn4N7nBi/IfUnCj7
wznbkK7eJrCx+hc3c/zxUlP7OBqJsU0GbqS06S0Gv9tSivh61/H7GDpNUPobTGPs
FWWaLXvYqyIPD9Pe/feesWnpaPUc0DtVp21PTT+v5EweB/ZbAeKJg1jQhr5mLark
ltFY1NeXr5dYh2tWuYViHX7C+cjxOPkWmu70pZ5HHzyBQkoP/ovVuQ7Uoz5acq5N
YllAy5p+/aQRbJN5pRCd4UI/4rMq1XrNwipaPLf3DWzz0tOGDsQASDcFPfMd4VKp
b4WTyfa0tnclRFGDvzCXblsyqVLayo6GcwAxRHket39a2tbEyhOjRqnbS0/WukRl
AgMBAAGjgZMwgZAwDAYDVR0TAQH/BAIwADAaBgNVHREEEzARgglsb2NhbGhvc3SH
BH8AAAEwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0PAQH/BAUDAwegADAdBgNV
HQ4EFgQUg24BZg1gjao2SbHvcmEwiug/WFowHwYDVR0jBBgwFoAU6tWU5wVvzJs6
xNj6vD+9UTk/fkcwDQYJKoZIhvcNAQELBQADggIBABkfXM5B+Yh/a3FgVuCHtTMz
koYEslhwFb7pp9Uyjora1reHOfcHUQm+6dMhOzQ+F0fn6M0MqKkfM+dZGRVv4Kq0
zwlZSfpTD277eFfnBdyjJiJPB400fzI3icHhUAm0k8wS9hsVmJZwAHR84N+x+hvd
5jXaSVAbkSNH1pZ389v6wgJF8QWBrIHjuFiLBdj6k/+IZAp7SgnsGrQQdVATl5Bl
L/iK7wdA/bqzi7nKnPC5krCn+wgq5WxkLXSM2qptRMuddRUwyhLQ/eskqxakdGYR
G/C5CH3PfmamUMoh89aA/QIhCqi+OJ61s91mAkprNibYxq1ykWwPzLhjh3eSDzAr
Zc/3a5WAKNtXxrmgOup+9/jpYOnwWHtm0nyZtch3VSS35/LAZpTu4LB8LD+RzULB
/gVrvNq5BjJotv2N/r4nB/jmY/i1Dh0lTRM9Yww3UUEZNp/XQ4kNneVxM1X4l8qC
yijsfLXHiQ5oSrejFmHx5tnCx8lh5KeO8XY/YiyQkOwDtODVpXoTkwO6dCjyXIgj
mWIDMrTLhEz7SnRSxfIrbYxJAU8RKq5GOtjRcKVN8IqRPiWy6MVZROaAA7Urnaxs
yrsxhziJFd7mHR1djXFc3ypvfXj9063V6jVVKFoWzYBvhtOMIUX8c6NQaVR1s1Pi
Kt6t5W0w1V6RBnCUtQEf
-----END CERTIFICATE-----

View File

@@ -1,14 +1,19 @@
-----BEGIN X509 CRL-----
MIICKTCB4gIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJTRTEeMBwGA1UE
CgwVR8O2dGVib3JnIEJpdCBGYWN0b3J5MRIwEAYDVQQHDAlHw7Z0ZWJvcmcxGjAY
BgNVBAgMEVbDpHN0cmEgR8O2dGFsYW5kMR4wHAYDVQQDDBVHw7Z0ZWJvcmcgQml0
IEZhY3RvcnkXDTE0MDcwNjAwMTUxOFoXDTE1MDcwNjAwMTUxOFowAKAvMC0wHwYD
VR0jBBgwFoAUV6hrNd+llELYw9FhchM/2QvO37UwCgYDVR0UBAMCAQAwDQYJKoZI
hvcNAQEFBQADggExAFoR5SJ/D0SWE9kGgvgotRFFeXfkgel9im/sQdHdUrKn/MGG
imT4upSBU1hTgDqy252DRUPrs3DXON/T4L8sWq7t7PfzlDbO6fV2Wkmsd43/yCPt
tK6dsrcnAhWFCamkBNVGi/RZt6da3y72Y7fo7cfzkK8UeF6UWgGBt4+raR3pmRIw
+FpDEYuUOXajvtIxMD0CMdeFo+w8/5RGxXyqR4sftnRrD2hX6aFljrejxP/Pa7M9
TvgKtUMxsOSauSWN7vg59+FE92PNqNa3KEquO7h5CI5yjr/+85K7o/6FGHOV4iD2
h/RQHI2l8AEpqDqJLZu5GvjN6mblhIYlo+uMTZOXHWxoIDDQLsOXJyCpHSNWjGRs
dP4OMD0/0ykppjogdOdUMEN/8smqtPumPzP/ykg=
MIIDCDCB8QIBATANBgkqhkiG9w0BAQsFADB9MR4wHAYDVQQDDBVHw7Z0ZWJvcmcg
Qml0IEZhY3RvcnkxHjAcBgNVBAoMFUfDtnRlYm9yZyBCaXQgRmFjdG9yeTESMBAG
A1UEBwwJR8O2dGVib3JnMRowGAYDVQQIDBFWw6RzdHJhIEfDtnRhbGFuZDELMAkG
A1UEBhMCU0UYDzIwMTUwMTE5MjMxODA5WhgPMjAxNjAxMTkyMzE4MDlaMACgOjA4
MB8GA1UdIwQYMBaAFOrVlOcFb8ybOsTY+rw/vVE5P35HMBUGA1UdFAQOAgxUvZCx
D9YrMF6sqNkwDQYJKoZIhvcNAQELBQADggIBALBQaaSdZDHi5MsnIRHsbz3J0FGb
NU7oI8wLXMWTNNF4rv9h5kVQCOwCYBOhIGTXkunM9HCZ22gTYVXus1uOBVoJR+1g
qvraZU1bhtziE3DRHCZDRhq3l8SV9/VZDWP/DryPuBgJgxE8Gaq8UGJ1u2QhWmaj
ZFET3bNiQwV8omI8Bi+D+aKpICt0+WixPAYhdf5pAb6dtGwh9Qy9J7hlH9FYyXMm
E5bNWjPjfJeluant0V2yXqaDFL2UnryeD03+YagkZ9ixkCxWgBXOjMCfZ0Bmpej9
jrJ4W/ki6W28eWrNbgIMJl4EDJyof2pOIGXkvSW+QjO7laxNevIZ9ofOHz8eMeXA
FOwJvLBjl4Jp03HyTh1jhCnr1y0PVYtvwv88ThBGbYGCKcVQlKCOa+C3dBQ9thIY
SU8yWhBWt6Bdrxf92GMJxpTqUbzyL8HoqwK4SI+gfua3J5YeBTErYKhD2an3Ra7T
UeisZJM7nGi1astGLE8V5rYtb74hNJbrR8I3CyE/Odsnk1Jq2kKMc5wQQeJLEsoX
lRtSdmYSXZ13IoKj0sJvm3jydSX42u1mtwZwHrKmcVVG5XqLKn/2x6Z8+5vjM9JK
AsuGlpGIiZDMg2wnY8q0yo0q+yPE5xi9NlOeCSRtY0DfpNLqWi8exrIwwplT26V8
cbzBujcnnznK9J6F
-----END X509 CRL-----

View File

@@ -1,32 +1,247 @@
Public Key Info:
Public Key Algorithm: RSA
Key Security Level: High (4096 bits)
modulus:
00:c8:b5:8c:80:84:05:5d:4e:e7:6b:34:4a:e9:4a:
4f:d5:2f:39:3e:ef:50:6a:d0:a9:21:4f:56:4c:0c:
1d:c5:b0:33:19:ad:fe:84:40:c0:f8:a3:c2:97:4b:
9e:b0:6d:dc:f6:14:da:c6:43:f2:07:e6:6c:19:6e:
aa:be:f6:18:e5:15:88:20:50:7a:6c:23:d1:15:7a:
ef:b6:78:ce:3c:af:0e:88:f9:e3:09:b3:00:be:0f:
91:68:02:3d:9d:93:ea:1d:e9:19:91:1d:ab:da:92:
b5:7b:cc:26:a9:34:8a:b2:2c:43:8e:24:68:bd:f2:
d5:09:b8:6b:28:73:b7:44:13:f8:14:df:40:89:14:
21:5f:c0:41:53:39:5d:76:75:be:8d:44:94:bf:a6:
ef:de:cb:3a:2c:8f:26:ed:60:0d:06:43:ce:2f:76:
a0:72:54:c6:be:24:bc:fa:ed:22:87:7a:7f:0a:8f:
29:28:c2:71:63:d3:ea:e4:a1:6b:eb:f0:dc:93:16:
e2:88:98:1f:00:0d:26:70:a9:57:a7:43:15:eb:fa:
9a:6f:9d:e5:4c:f6:e5:af:af:f9:ac:33:77:5a:8d:
06:b5:f7:ca:0d:38:8d:8f:06:29:d6:2f:e3:7f:89:
19:5c:ef:85:f9:24:a8:d0:08:a7:bd:4d:e2:30:28:
4c:19:a9:9a:c1:43:ce:7e:0d:ee:70:62:fc:87:d4:
9c:28:fb:c3:39:db:90:ae:de:26:b0:b1:fa:17:37:
73:fc:f1:52:53:fb:38:1a:89:b1:4d:06:6e:a4:b4:
e9:2d:06:bf:db:52:8a:f8:7a:d7:f1:fb:18:3a:4d:
50:fa:1b:4c:63:ec:15:65:9a:2d:7b:d8:ab:22:0f:
0f:d3:de:fd:f7:9e:b1:69:e9:68:f5:1c:d0:3b:55:
a7:6d:4f:4d:3f:af:e4:4c:1e:07:f6:5b:01:e2:89:
83:58:d0:86:be:66:2d:aa:e4:96:d1:58:d4:d7:97:
af:97:58:87:6b:56:b9:85:62:1d:7e:c2:f9:c8:f1:
38:f9:16:9a:ee:f4:a5:9e:47:1f:3c:81:42:4a:0f:
fe:8b:d5:b9:0e:d4:a3:3e:5a:72:ae:4d:62:59:40:
cb:9a:7e:fd:a4:11:6c:93:79:a5:10:9d:e1:42:3f:
e2:b3:2a:d5:7a:cd:c2:2a:5a:3c:b7:f7:0d:6c:f3:
d2:d3:86:0e:c4:00:48:37:05:3d:f3:1d:e1:52:a9:
6f:85:93:c9:f6:b4:b6:77:25:44:51:83:bf:30:97:
6e:5b:32:a9:52:da:ca:8e:86:73:00:31:44:79:1e:
b7:7f:5a:da:d6:c4:ca:13:a3:46:a9:db:4b:4f:d6:
ba:44:65:
public exponent:
01:00:01:
private exponent:
00:ab:b6:8f:07:20:6c:0a:81:48:01:f2:d2:28:36:
f8:87:f6:07:ee:ed:05:8a:ed:0b:d5:c0:af:9f:0e:
3a:a6:77:f1:b0:05:a9:1b:72:de:e5:9a:0d:10:2d:
95:0d:72:60:b9:73:ec:1b:f5:e3:6a:98:db:61:02:
ca:37:e8:0a:32:6b:b2:73:9b:06:b1:e0:0e:b0:66:
e6:10:e1:d7:e8:19:08:c1:6d:b0:ad:0a:a5:16:f5:
f0:bb:e0:36:e0:ca:bc:60:6c:9e:00:06:37:92:1f:
33:ff:3f:10:d0:70:70:3c:b1:1b:f6:ce:f5:81:94:
f0:68:89:8b:bc:27:0b:e2:c5:86:d8:2c:8c:64:fa:
80:f7:0f:5f:94:19:1e:fa:0b:10:44:82:29:14:b2:
78:c3:ce:70:ed:d0:67:48:6c:85:98:17:88:9e:18:
ca:7a:26:47:86:43:9e:1e:bb:a4:6f:ee:00:24:83:
88:7e:23:a4:a8:f8:94:1c:98:71:e1:80:c6:e5:5c:
8d:e0:b8:ec:c4:b9:7b:6a:a5:a6:a5:26:02:ea:4a:
13:b8:6c:88:d1:c8:e5:86:e2:72:23:be:40:5d:8b:
0b:f3:c0:93:ef:ad:b3:86:6a:8a:7e:81:a8:0b:e3:
ba:7c:45:3c:04:84:35:9d:0d:da:9c:5d:b6:a6:e6:
c2:fb:28:d8:db:6d:8e:27:aa:42:d7:91:37:d9:6b:
51:d2:54:c2:b3:9a:be:a7:5b:20:5c:59:b7:69:0c:
17:27:34:26:4d:7e:68:95:28:8d:db:bb:88:07:12:
68:94:7f:e1:a9:91:e3:fe:8f:21:fb:3a:43:40:ff:
81:cf:ae:a6:7d:2d:67:4c:fc:82:d1:dc:84:e2:1b:
dc:07:5a:2c:ab:6b:68:8c:b2:8d:1b:97:b0:22:6d:
2a:5e:72:bc:b5:b8:93:a8:e5:8a:0e:a2:4b:b0:76:
55:a1:14:ae:6e:68:25:89:9c:68:cf:cc:6c:f3:bc:
4a:96:bc:b2:1a:01:db:3c:da:37:a2:81:ff:55:dd:
b9:a4:90:79:55:94:bb:be:ca:d7:86:89:ae:68:a5:
9f:f4:5a:2c:d9:33:ed:fa:22:f7:1d:b2:43:ce:e6:
61:37:e6:eb:25:44:89:bd:43:4a:3c:10:a8:0a:2f:
40:c3:a8:75:73:17:42:22:a9:73:73:95:e8:7e:26:
9c:30:c9:13:04:0f:e3:78:7f:75:8d:30:9a:d9:1b:
6d:48:2b:09:d8:80:53:27:82:a3:97:e1:d8:2d:35:
59:7d:60:da:e8:27:5e:a7:ee:ff:6b:90:ca:74:9a:
27:67:89:08:17:88:6c:b2:12:18:ba:ac:e2:72:ae:
a5:ea:9d:
prime1:
00:d3:b9:bb:17:8e:2c:2c:e9:f2:45:6b:41:e5:2e:
63:9e:8c:cc:d3:06:c7:b5:49:60:2f:78:98:80:73:
09:e3:17:5f:44:89:7c:74:8d:52:6f:de:b7:56:a3:
f1:6c:fb:59:be:4c:df:79:22:df:4f:21:6f:f2:b2:
54:3b:6d:83:26:e6:5b:8f:93:25:dc:c2:98:5d:3b:
57:b3:b7:e1:d5:85:03:f9:b8:ca:26:cb:b4:35:41:
50:df:96:05:75:c2:f1:98:06:26:52:bd:87:e4:ec:
88:40:1d:c3:b4:de:b2:e8:21:1d:38:dd:7a:0a:b6:
f9:fc:ce:7a:1d:33:0a:cb:ea:14:95:8b:9a:50:7e:
b4:34:01:42:ee:5b:14:34:45:36:c3:b9:2e:16:e1:
a0:f0:e2:11:7a:6c:1e:d5:b2:84:03:d8:07:49:27:
e5:fc:fe:4f:01:66:fe:7d:40:fa:4c:f4:86:06:dc:
31:33:bb:30:b1:52:f6:9a:ee:6f:22:a8:68:66:52:
3a:58:35:15:27:6a:70:67:2b:2c:8b:7c:b0:e0:87:
85:49:c7:51:6e:5d:4c:a1:6b:69:2b:b2:9f:a7:9a:
2e:08:8a:ec:e4:36:ad:83:46:55:92:f9:ef:05:14:
ac:24:28:7d:e5:72:b9:49:ce:1b:d3:40:4b:fe:16:
7a:03:
prime2:
00:f2:ae:15:0a:b2:f6:29:ee:5c:eb:fd:76:27:68:
b4:5a:95:21:f8:32:4d:92:53:a3:c0:ce:ab:fc:b0:
cd:f7:58:1c:35:88:90:42:e2:9f:65:ec:7f:01:98:
69:bc:69:84:4c:3a:68:0d:c8:09:a2:88:8f:c7:c7:
a1:35:f0:95:c1:2d:67:23:95:50:fc:cb:97:a6:1f:
67:82:02:9e:3a:dc:b0:1b:77:49:50:bd:ce:be:8f:
28:cf:29:c2:2e:82:32:5a:45:7e:cb:c1:44:33:af:
7e:db:8e:68:c9:01:57:8e:cb:e0:ac:1d:5e:d5:21:
d6:d4:d1:40:81:68:7f:1e:55:24:c2:bb:9d:3d:1b:
cd:6c:ca:11:b9:13:cf:ad:37:26:3d:22:e5:d0:3a:
5f:24:aa:0b:f6:c7:cb:7b:0c:12:bd:5b:d5:c2:25:
5f:64:38:10:eb:5f:40:2d:0d:0f:b4:90:54:24:d9:
70:65:1f:d5:9d:ae:9d:ef:a1:36:87:3d:c6:41:a7:
56:5e:fe:33:1d:43:a0:c5:98:3e:ac:b1:23:4c:0e:
a2:5d:50:82:22:b6:ba:b0:b9:5e:84:c4:2b:a9:0d:
15:f9:26:32:57:49:6b:1f:fc:d6:4d:8e:fc:f9:13:
c7:a5:80:53:82:83:49:92:34:dc:fe:82:52:49:4b:
2f:77:
coefficient:
00:99:7f:81:87:b0:27:9e:72:29:8d:76:a7:25:72:
e5:60:9e:86:80:fe:2a:d3:c4:07:96:d4:de:e4:a0:
c6:dc:89:11:4a:ba:0e:06:1e:7e:bb:bc:69:2e:40:
fd:4a:cd:65:34:37:d0:0c:9e:f9:b3:01:05:b3:6d:
8e:9f:7d:72:36:56:7f:b1:ca:e4:e8:76:fd:99:12:
9d:ef:3c:f6:1b:4a:80:7b:92:c5:26:c1:50:f2:6d:
a8:86:4e:3b:5c:10:14:cd:21:db:c8:38:d1:56:c9:
72:a8:00:45:8c:71:d3:35:20:0c:a6:83:5e:33:9c:
b2:f7:96:1c:35:30:8d:47:1b:e8:f8:84:df:9e:e4:
32:d2:c0:da:1b:ed:27:db:a4:6b:0d:61:bb:67:6a:
24:fc:93:9d:46:1f:91:34:21:61:cc:95:7c:0e:d6:
64:96:4e:2e:06:40:57:df:b5:e2:57:30:c5:ff:d5:
f5:89:08:ce:98:4e:01:81:30:dc:29:f6:05:98:7f:
02:1c:ea:f5:a7:13:47:7c:98:81:13:a9:5d:9b:80:
a8:2e:c9:5e:ae:b3:df:c6:02:81:19:84:1c:21:df:
88:b0:96:7a:84:e8:ef:91:f2:69:50:f1:93:3c:31:
94:38:10:01:6c:3d:fa:ca:94:5f:c5:9a:d6:43:77:
fd:36:
exp1:
5a:1a:26:66:a5:d8:62:dc:c4:7f:8a:5f:a6:9a:91:
46:7e:01:04:a5:54:03:f0:50:00:a3:bf:b2:9a:42:
50:e4:37:64:c1:65:b7:6c:14:f3:0a:c2:d7:1a:39:
b9:f9:55:7d:c8:d5:29:86:0f:a6:6b:42:6a:f4:3d:
28:75:38:65:2b:8f:37:90:95:82:57:45:e4:17:47:
01:72:75:a7:1a:4e:50:cd:b5:29:c1:72:d5:5f:10:
7d:b6:28:68:af:27:85:5c:c8:9e:59:8e:25:7c:0b:
97:cd:92:e5:bc:08:43:05:24:9c:78:94:61:4e:f1:
20:06:f6:ac:ca:76:03:c5:80:31:0c:12:05:c3:86:
08:c3:a8:aa:2b:14:74:12:28:76:2e:ae:50:8a:80:
a5:42:6f:d1:bb:85:7b:c7:d6:aa:94:1a:b5:a5:47:
18:dd:58:dd:ed:e8:cb:81:05:84:0e:63:a3:33:bd:
b4:98:2a:e6:ab:b9:ca:c1:41:86:6c:48:d1:92:98:
c5:c5:01:55:9e:d4:6a:f3:52:a7:a1:ce:dc:1e:90:
38:8f:9c:35:ed:7a:99:0f:4e:d8:8b:d1:a9:cf:d1:
5b:4d:53:f6:e2:d6:00:91:c4:05:22:5b:f7:bb:8e:
6e:6c:d4:f6:fa:55:a3:f5:14:9e:58:34:7e:d3:2d:
e5:
exp2:
00:d4:73:ee:5b:e5:7c:78:0d:03:30:5e:3e:b8:15:
83:fe:e1:1c:52:0a:c7:ba:58:9a:bd:4a:b1:6a:90:
e6:5f:a9:b5:58:70:6b:cb:02:57:05:85:db:52:6c:
39:f5:06:73:19:c0:f0:23:50:82:8e:d5:fa:f9:ad:
1c:c6:fa:0e:40:86:1d:0b:1b:c7:40:e8:83:a4:9d:
4d:a2:95:e4:2c:27:d3:cc:d7:cb:49:72:41:b2:b5:
e5:c6:27:0a:a6:68:b8:e8:2a:7a:ec:aa:0f:a9:27:
f4:69:05:ed:5a:3b:60:89:d4:96:a6:57:38:14:62:
81:8e:e2:7a:14:50:85:28:ea:e5:39:cd:c1:c4:23:
b8:dd:8a:b1:a5:53:60:b6:85:d7:66:22:4d:0a:d4:
6f:42:cb:d3:99:82:23:f3:5f:08:70:da:1a:f2:d2:
81:06:5d:c7:02:27:eb:29:05:8c:40:49:32:40:e8:
52:eb:54:c9:50:37:f4:70:3a:b4:60:05:86:b1:27:
6c:07:d1:86:b2:11:e8:87:d3:d6:da:9c:61:08:4e:
af:79:fd:e7:4f:b8:a5:0b:dd:dc:8f:62:a5:ce:63:
74:5f:20:3f:66:07:12:4f:78:ab:ac:4e:87:cd:0e:
b1:f3:83:ba:98:41:93:80:f9:e2:96:b5:44:90:81:
cf:d5:
Public Key ID: 83:6E:01:66:0D:60:8D:AA:36:49:B1:EF:72:61:30:8A:E8:3F:58:5A
Public key's random art:
+--[ RSA 4096]----+
| o+. |
| o. .o |
| .o + . |
|.= o . . |
|* = o S |
|=+ E . . . |
|o O . o |
| = + . |
| +.. |
+-----------------+
-----BEGIN RSA PRIVATE KEY-----
MIIFegIBAAKCATEAqy/k1q7ne2YLyfdQf4Watpk82vuCUXSwN7Sh8bPtgvaiYqAT
hBci6aInppvmNdZoXC5uGLLpYR1I+06R0HVV6U3N+bSM9JewMmGjB9pQE/kXiLDt
RR9qknd95d9jmJ4mP4N3WOw4hbCc4W+X6q8wL4q2Mfi/7idNEnRB0jn8SF4QkQKa
3EBLhe2Y0lWpDaVGq4Y7EfPi0DMfV2XZYMpmbr8mf2ArEfrqIYgoyuRtG5pVNtkj
zfxIrYYlEuncUAgSmzzuQ6K19mRB4yHXmexNbV74cKK2zIOIJj5xbsPm5NKfTtzi
kxijRVdTkEcbCP6Bo1hk8/QbTzyD2FGDhzWi59gaGQ7Qbg/nzf7/s1zZ5lY697ej
+0nAR8ZPymQVOAA5z5kFJBSCrKizCJbJOxmGpwIDAQABAoIBMEYSQq+FxIcETEsN
isvIGn4n13sjfEJvazpCHA/dwJjc77dDIvnspoPPtj9CjUb4Tpt8/d/qTBFOdXM4
xAL6JIJtMKTHvPLZrGvS/3LVb/Xc/j8bfOPQakUt2EDh4di0058n5xBlEPXYRz8X
+K09rJGpS87ErXuEZs3o/JBa3+OXbVyOHmmNf9yAVw+xtJM7VeQb+83rcKobt0X6
aPm1lYkc4oiYcaPa/Vz39jdP5eBekrIbJ4/uLDhZ1UKBxEn7vW+J5cGGqJpksC18
UHrTpm3T+SXKnEU8eGFHH3ukNuESTPa5Ywp8Jgkrs23HjUi26nwxqsPuXzVEL++/
AgTX8NXXy4c8HQ7d6WDC9wL6b7h75uStrdRrTRPPYvWXJRfwB+WF1IE2MpAtiG96
ORL445ECgZkA3kwxkP27KtpFRVdPc3S+pb7WCoNNx40F8OUKiCht1w1vRnHJY3N+
K5Xv1lMIAU0LsVME5QDGmu1O2bhb/95NKNkHm54l5NhuwSMDL4iP4h0CUb4XbD/O
02t7xN6kh9wmUDkPOPX8IuNupc/ipK9r98xBdxW9dCwXcixjmbihrSi1xG1qKqSH
fdQlp4mjAk9PW5SEJ6A3XBsCgZkAxSP/+maMAT8jhNe5ISgeMfLqbf1S6xqIYGtn
G6VZWGOCjm1MdSlDk4HmkgnLOJLZseKnuNVOEjiolqOgpaPdkacdI2RdUhoLXazA
7ZQ6YfG15pwCzS/xcuhURByhBiP7LvwfSjTA5iESsgnFu9WXbxMf2Py7ttpooHDk
vreawetHrSe00ZAotV0Y1zWHeLUkvB9JYjWJkGUCgZh3mY7hb2IQADdcw0jf2WPp
UO9f4J0sS9dycAhmkTnnhjbkM7wiYbT0RksviKS57nXoBb6QZwZzvFG8GdCFrVje
LUZGl6PTqA6OowPHwzWGdg0VZay8dOgWH7ILja3/2pZSeyCRiOcK7kpNs7dA25C/
VYmzRGMV6dMDfpZn0kpKG6kP8b45CyRKsqU/qnGlgH41y4X+u9iNGQKBmDhYKLI9
muhETneKXHwwhijKf9w92fQeGjpMmCSbpqSPlGH2tTKLP6gqKWc162JY/mVMHZnD
RVQJ441iaOjOo+YcOETES7PKg9MbK+z7P5B8z/uAPYC9oimwY10wPYmOwRzVrG++
Eh/z9e7ywhQEYQq7EZAn89K4K80nVwl3WoDwKQVBYvm+VRQ47n+b6gNC6qPl4Uhz
esTNAoGYCBL0d9Rhl3ViQCGM/qkaM9YJ7nm1EJ9g9rCOCQCQOK6J40hY8q+BtQph
LfYQSqyoN2p/M3toHEeXtnKmgO4+9o7g52zKOBvN566+tEEZUirsane29UuaZBXp
ixW1i7VOtDOU//oeWvJnd1oufYIi9rZ/F1pK+aqNHgknCxQ3Ogolz3n23TdWcFXj
FGx/3NV/ZgtZ3UVTxXM=
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
-----END RSA PRIVATE KEY-----

View File

@@ -1,24 +1,33 @@
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----

Some files were not shown because too many files have changed in this diff Show More