├── tests
├── tmp
│ └── README.md
├── plugins
│ └── test-plugin.php
├── Unit
│ ├── WpTestsStarterTest.php
│ └── Helper
│ │ ├── SaltGeneratorTest.php
│ │ └── DbUrlParserTest.php
├── bootstrap.dist.php
└── WpIntegration
│ ├── WpTestsStarterDefaultConstantsTest.php
│ └── WpTestsStarterTest.php
├── .gitignore
├── src
├── Exception
│ ├── Throwable.php
│ └── RuntimeException.php
├── Helper
│ ├── SaltGenerator.php
│ └── DbUrlParser.php
└── WpTestsStarter.php
├── phpunit.xml.dist
├── phpunit-integration.xml.dist
├── phpcs.xml.dist
├── CHANGELOG.md
├── composer.json
├── CONTRIBUTING.md
├── README.md
├── .ddev
└── config.yaml
└── LICENSE
/tests/tmp/README.md:
--------------------------------------------------------------------------------
1 | This directory is used during unit tests to write files in.
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .ddev/*.local.yaml
2 | vendor/
3 | tmp/
4 | composer.lock
5 | phpunit-integration.xml
6 | phpunit.xml
7 |
--------------------------------------------------------------------------------
/tests/plugins/test-plugin.php:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 | ./tests/Unit
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/tests/bootstrap.dist.php:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 | ./tests/WpIntegration
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/Helper/SaltGenerator.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | ./src/
4 | ./tests/
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
21 |
22 |
23 |
24 | vendor/*
25 |
26 |
--------------------------------------------------------------------------------
/tests/Unit/Helper/SaltGeneratorTest.php:
--------------------------------------------------------------------------------
1 | generateSalt($length);
20 |
21 | self::assertMatchesRegularExpression(
22 | '~^[\x20-\x7E]{' . preg_quote((string)$length) . '}$~',
23 | $salt
24 | );
25 | }
26 |
27 | public function generateSaltTestProvider(): array
28 | {
29 | return [
30 | ['length' => 1],
31 | ['length' => 10],
32 | ['length' => 42],
33 | ];
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog of inpsyde/wp-tests-starter
2 |
3 | ## main
4 | * Introduces Changelog
5 | * Use new default branch `main`
6 | * Raise minimum PHP version to 7.4
7 | * Behavioral changes
8 | * `WpTestsStarter::useConst()` does not define the constant immediately like `::defineConst()` did
9 | * API changes
10 | * Remove `Common\SaltGeneratorInterface`
11 | * Rename `Common\*` to `Helper\*`
12 | * Introduce `Helper\DbUrlParser`
13 | * Rename mutator methods `WpTestsStarter::set*()` and `::define*()` to `::use*()`
14 | * Rename method `WpTestsStarter::defineConst()` to `::useConst()`
15 | * Add method `WpTestsStarter::addLivePlugin()`
16 | * Add methods `WpTestsStarter::addFilter()` and `::addAction()` to mimic `add_filter()` and `add_action()` before WP is loaded
17 | * Internal refactoring
18 | * Move phpunit*.xml.dist to root directory
19 | * Reformat code
20 | * Add DDEV as development environment
21 | * Update dev dependencies
22 |
23 | ## 1.0.2
24 | * Add `WpTestsStarter::getConfigFile()`
25 | * Fix: write constants into test config file #3
26 |
27 | ## 1.0.1
28 | * typo fixes
29 | * License added
30 |
31 | ## 1.0.0
32 | * initial release
33 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "inpsyde/wp-tests-starter",
3 | "description": "A package that helps you setting up WordPress integration test environments quickly.",
4 | "keywords": [
5 | "WordPress",
6 | "Tests",
7 | "Integration tests"
8 | ],
9 | "license": "GPL-2.0-or-later",
10 | "authors": [
11 | {
12 | "name": "Syde GmbH",
13 | "homepage": "https://syde.com/",
14 | "email": "hello@syde.com",
15 | "role": "Company"
16 | },
17 | {
18 | "name": "David Naber",
19 | "email": "d.naber@syde.com",
20 | "role": "Developer"
21 | }
22 | ],
23 | "require": {
24 | "php": ">=7.4"
25 | },
26 | "require-dev": {
27 | "inpsyde/php-coding-standards": "^1.0",
28 | "phpunit/phpunit": "^9.5",
29 | "wordpress/wordpress": "^6.0",
30 | "yoast/phpunit-polyfills": "^1.0"
31 | },
32 | "autoload": {
33 | "psr-4": {
34 | "Inpsyde\\WpTestsStarter\\": "src/"
35 | }
36 | },
37 | "config": {
38 | "allow-plugins": {
39 | "dealerdirect/phpcodesniffer-composer-installer": true
40 | }
41 | },
42 | "repositories": [
43 | {
44 | "type": "vcs",
45 | "url": "https://github.com/WordPress/wordpress-develop"
46 | }
47 | ]
48 | }
49 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Thanks for contributing to WP Tests Starter
2 |
3 | # Getting Started
4 | * Make sure you have a [GitHub account](https://github.com/signup/free).
5 | * See if your issue has been discussed (or even fixed) earlier. You can [search for existing issues](https://github.com/inpsyde/WP-Tests-Starter/issues?utf8=%E2%9C%93&q=is%3Aissue).
6 | * Assuming it does not already exist, [create a new issue](https://github.com/inpsyde/WP-Tests-Starter/issues/new).
7 | * Clearly describe the issue including steps to reproduce when it is a bug.
8 | * Make sure you fill in the earliest version that you know has the issue.
9 | * Fork the repository on GitHub.
10 |
11 | # Making Changes
12 | * Create a topic branch from where you want to start your work.
13 | * This is usually the `master` branch.
14 | * To quickly create a topic branch based on the `master` branch:
15 | * `git checkout -b issue/%YOUR-ISSUE-NUMBER%_%DESCRIPTIVE-TITLE% master`
16 | * a good example is `issue/118_html_lang_attribute`
17 | * Please avoid working directly on the `master` branch.
18 | * Make commits of logical units.
19 | * Make sure your commit messages are descriptive.
20 |
21 | # Submitting Changes
22 | * Push your changes to the according topic branch in your fork of the repository.
23 | * [Create a pull request](https://github.com/inpsyde/WP-Tests-Starter/compare) to our repository.
24 | * Wait for feedback. We look at pull requests on a regular basis.
25 |
26 | # License
27 | By contributing code to _WP Tests Starter_, you grant its use under this [License](LICENSE).
28 |
--------------------------------------------------------------------------------
/src/Helper/DbUrlParser.php:
--------------------------------------------------------------------------------
1 | null,
30 | 'user' => null,
31 | 'password' => null,
32 | 'db' => null,
33 | 'table_prefix' => null,
34 | 'charset' => null,
35 | 'collation' => null,
36 | ];
37 |
38 | $parts = parse_url($dbUrl);
39 | if (! is_array($parts)) {
40 | throw new RuntimeException("Please provide a valid dbUrl");
41 | }
42 |
43 | if (array_key_exists('scheme', $parts) && $parts['scheme'] !== 'mysql') {
44 | throw new RuntimeException("Currently only 'mysql' databases are supported");
45 | }
46 |
47 | if (array_key_exists('host', $parts)) {
48 | $credentials['host'] = $parts['host'];
49 | if (array_key_exists('port', $parts)) {
50 | $credentials['host'] .= ':' . $parts['port'];
51 | }
52 | }
53 |
54 | array_key_exists('user', $parts) and $credentials['user'] = $parts['user'];
55 | array_key_exists('pass', $parts) and $credentials['password'] = $parts['pass'];
56 | array_key_exists('path', $parts) and $credentials['db'] = trim($parts['path'], '/');
57 |
58 | if (! array_key_exists('query', $parts) || empty($parts['query'])) {
59 | return $credentials;
60 | }
61 |
62 | $query = [];
63 | parse_str($parts['query'], $query);
64 |
65 | array_key_exists('table_prefix', $query) and $credentials['table_prefix'] = $query['table_prefix'];
66 | array_key_exists('charset', $query) and $credentials['charset'] = $query['charset'];
67 | array_key_exists('collation', $query) and $credentials['collation'] = $query['collation'];
68 |
69 | return $credentials;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tests/WpIntegration/WpTestsStarterDefaultConstantsTest.php:
--------------------------------------------------------------------------------
1 | bootstrap();
25 |
26 | self::assertFileExists(
27 | $wpTestConfig
28 | );
29 | $config_data = file_get_contents($wpTestConfig);
30 |
31 | self::assertMatchesRegularExpression(
32 | '~define\(\s\'ABSPATH\',\s\'[^\']+\'~',
33 | $config_data
34 | );
35 | self::assertInstanceOf(
36 | \wpdb::class,
37 | $GLOBALS['wpdb']
38 | );
39 | self::assertTrue(
40 | $GLOBALS['wpdb']->check_connection()
41 | );
42 | }
43 |
44 | /**
45 | * @runInSeparateProcess
46 | */
47 | public function testBoostrapWithSetters(): void
48 | {
49 | $baseDir = dirname(dirname(__DIR__)) . '/vendor/wordpress/wordpress';
50 | $wpTestConfig = $baseDir . '/wp-tests-config.php';
51 |
52 | $credentials = (new DbUrlParser())
53 | ->parse(getenv('WPTS_DB_URL'));
54 |
55 | $testee = new WpTestsStarter($baseDir);
56 |
57 | $credentials['host'] and $testee->useDbHost($credentials['host']);
58 | $credentials['user'] and $testee->useDbUser($credentials['user']);
59 | $credentials['password'] and $testee->useDbPassword($credentials['password']);
60 | $credentials['db'] and $testee->useDbName($credentials['db']);
61 | $credentials['table_prefix'] and $testee->useTablePrefix($credentials['table_prefix']);
62 | $credentials['charset'] and $testee->useDbCharset($credentials['charset']);
63 | $credentials['collation'] and $testee->useDbCollation($credentials['collation']);
64 | $testee->bootstrap();
65 |
66 | self::assertFileExists(
67 | $wpTestConfig
68 | );
69 | $config_data = file_get_contents($wpTestConfig);
70 |
71 | self::assertMatchesRegularExpression(
72 | '~define\(\s\'ABSPATH\',\s\'[^\']+\'~',
73 | $config_data
74 | );
75 | self::assertInstanceOf(
76 | \wpdb::class,
77 | $GLOBALS['wpdb']
78 | );
79 | self::assertTrue(
80 | $GLOBALS['wpdb']->check_connection()
81 | );
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/tests/Unit/Helper/DbUrlParserTest.php:
--------------------------------------------------------------------------------
1 | parse($dbUrl)
23 | );
24 | }
25 |
26 | /**
27 | * @see testParse
28 | */
29 | public function parseData(): iterable
30 | {
31 |
32 | yield 'Test entire parameter set' => [
33 | 'dbUrl' => 'mysql://john:T0p3ecr3t@localhost/wordpress?table_prefix=wp_tests_&charset=iso88591&collation=latin1_swedish_ci',
34 | 'expected' => [
35 | 'user' => 'john',
36 | 'host' => 'localhost',
37 | 'password' => 'T0p3ecr3t',
38 | 'db' => 'wordpress',
39 | 'table_prefix' => 'wp_tests_',
40 | 'charset' => 'iso88591',
41 | 'collation' => 'latin1_swedish_ci'
42 | ]
43 | ];
44 |
45 | yield 'Test entire parameter set with port number' => [
46 | 'dbUrl' => 'mysql://john:T0p3ecr3t@localhost:3306/wordpress?table_prefix=wp_tests_&charset=iso88591&collation=latin1_swedish_ci',
47 | 'expected' => [
48 | 'user' => 'john',
49 | 'host' => 'localhost:3306',
50 | 'password' => 'T0p3ecr3t',
51 | 'db' => 'wordpress',
52 | 'table_prefix' => 'wp_tests_',
53 | 'charset' => 'iso88591',
54 | 'collation' => 'latin1_swedish_ci'
55 | ]
56 | ];
57 |
58 | yield 'Only user and host' => [
59 | 'dbUrl' => 'mysql://john@localhost',
60 | 'expected' => [
61 | 'user' => 'john',
62 | 'host' => 'localhost',
63 | 'password' => null,
64 | 'db' => null,
65 | 'table_prefix' => null,
66 | 'charset' => null,
67 | 'collation' => null,
68 | ]
69 | ];
70 |
71 | yield 'Nothing at all' => [
72 | 'dbUrl' => 'mysql:',
73 | 'expected' => [
74 | 'user' => null,
75 | 'host' => null,
76 | 'password' => null,
77 | 'db' => null,
78 | 'table_prefix' => null,
79 | 'charset' => null,
80 | 'collation' => null,
81 | ]
82 | ];
83 | }
84 |
85 | /**
86 | * @dataProvider parseThrowsExceptionData
87 | */
88 | public function testParseThrowsException(string $url, string $expectedMessage): void
89 | {
90 | $testee = new DbUrlParser();
91 |
92 | self::expectException(RuntimeException::class);
93 | self::expectExceptionMessage($expectedMessage);
94 |
95 | $testee->parse($url);
96 | }
97 |
98 | /**
99 | * @see testParseThrowsException
100 | */
101 | public function parseThrowsExceptionData(): iterable
102 | {
103 |
104 | yield 'Test invalid URL' => [
105 | 'mysql://',
106 | "Please provide a valid dbUrl",
107 | ];
108 |
109 | yield 'Test unsupported scheme' => [
110 | 'mssql:',
111 | "Currently only 'mysql' databases are supported",
112 | ];
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/tests/WpIntegration/WpTestsStarterTest.php:
--------------------------------------------------------------------------------
1 | $arg;
33 | $testee->useWpPluginDir(self::$pluginDir)
34 | ->addActivePlugin(self::$testPlugin)
35 | ->addLivePlugin(static function(): void {
36 | defined('WPTS_LIVE_PLUGIN_RUN') or define('WPTS_LIVE_PLUGIN_RUN', true);
37 | })
38 | ->addFilter('post_link', $dynamicListener)
39 | ->addAction('template_redirect', $dynamicListener, 30)
40 | ->bootstrap();
41 |
42 | // test if the environment is available
43 | self::assertTrue(
44 | class_exists(\WP_UnitTestCase::class),
45 | 'Class \WP_UnitTestCase does not exist.'
46 | );
47 |
48 | $this->wpDbAssertions();
49 | $this->installedAssertions();
50 | $this->pluginAssertions();
51 | $this->livePluginAssertions();
52 | $this->listenerAssertion($dynamicListener);
53 | }
54 |
55 | private function wpDbAssertions(): void
56 | {
57 | self::assertInstanceOf(
58 | \wpdb::class,
59 | $GLOBALS['wpdb']
60 | );
61 | self::assertTrue(
62 | $GLOBALS['wpdb']->check_connection()
63 | );
64 | }
65 |
66 | private function installedAssertions(): void
67 | {
68 | $dbTables = $GLOBALS['wpdb']->get_results(
69 | 'SHOW TABLES',
70 | \ARRAY_N
71 | );
72 | $tablesFlat = [];
73 | foreach ($dbTables as $row) {
74 | $tablesFlat[] = $row[0];
75 | }
76 |
77 | $optionTable = $GLOBALS['table_prefix'] . 'options';
78 | self::assertTrue(
79 | in_array($optionTable, $tablesFlat),
80 | "Table {$optionTable} does not exist!"
81 | );
82 | }
83 |
84 | private function pluginAssertions(): void
85 | {
86 | self::assertArrayHasKey(
87 | 'wp_tests_options',
88 | $GLOBALS
89 | );
90 | self::assertArrayHasKey(
91 | 'active_plugins',
92 | $GLOBALS['wp_tests_options']
93 | );
94 | self::assertContains(
95 | self::$testPlugin,
96 | $GLOBALS['wp_tests_options']['active_plugins']
97 | );
98 | self::assertTrue(
99 | defined('WPTS_TEST_PLUGIN_LOADED')
100 | );
101 | }
102 |
103 | private function livePluginAssertions(): void
104 | {
105 | self::assertFileExists(
106 | WPMU_PLUGIN_DIR . '/wp-tests-starter-live-plugin.php'
107 | );
108 | self::assertTrue(
109 | defined('WPTS_LIVE_PLUGIN_RUN')
110 | );
111 | }
112 |
113 | private function listenerAssertion(callable $listener): void
114 | {
115 | self::assertSame(
116 | 10,
117 | has_filter('post_link', $listener)
118 | );
119 | self::assertSame(
120 | 30,
121 | has_action('template_redirect', $listener)
122 | );
123 | // test that we don't mess up the structure of $GLOBALS['wp_filter']
124 | self::assertSame(
125 | 9,
126 | has_filter('the_content', 'do_blocks')
127 | );
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WP Tests Starter
2 | Note: You're reading the documentation for the development branch towards version 2.0.0. You'll find the documentation
3 | for version 1.0 at the branch [version-1](https://github.com/inpsyde/WP-Tests-Starter/tree/version-1)
4 |
5 | Wp Tests starter is a library that assist you in setting up _integration tests_ for your plugin or library with WordPress
6 | core using the official [wordpress-develop repository](https://github.com/inpsyde/wordpress-dev). The main difference to
7 | unit tests is that you don't need (and typically don't want) to mock any WordPress function. Instead, you have a fully
8 | booted WordPress core in place with an actual connection to a database server.
9 |
10 | So if you're mapping your objects to WordPress posts in a ORM-style way your integration test would look like this:
11 |
12 | ```php
13 | public function testPersistBook(): void {
14 |
15 | $book = new Book('The Da Vinci Code', 'Dan Brown', '2003');
16 | $testee = new BookRepository($GLOBALS['wpdb']);
17 | $testee->persist($book); // maps book to WP_Post object and post meta
18 |
19 | self::assertGreaterThan(0, $book->id());
20 |
21 | $wpPost = get_post($book->id());
22 |
23 | self::assertSame('The Da Vinci Code', $wpPost->post_title);
24 | self::assertSame('2003', get_post_meta($book->id(), '_publishing_year', true));
25 | self::assertSame('Dan Brown', get_post_meta($book->id(), '_author', true));
26 | }
27 | ```
28 |
29 | No mocks required. Just WordPress working inside a PHPUnit test case.
30 |
31 | ## Installation
32 |
33 | In order to use Wp Tests Starter you need: a PHP environment with Composer and a MySQL server with a _dedicated test database_. This database should be completely ephemeral, so do not use any database that contains important data. You'll also need the following four Composer packages installed as dev dependencies:
34 |
35 | * `inpsyde/wp-tests-starter`
36 | * `yoast/phpunit-polyfills`
37 | * `wordpress/wordpress` taken from [wordpress-develop repository](https://github.com/inpsyde/wordpress-dev)
38 | * `phpunit/phpunit`
39 |
40 | As the last one is not available on packagist.org, you'll have to add the repo manually to your composer.json file by adding:
41 |
42 | ```json
43 | "repositories": [
44 | {
45 | "type": "vcs",
46 | "url": "https://github.com/WordPress/wordpress-develop"
47 | }
48 | ]
49 | ```
50 |
51 | Now you can run
52 |
53 | composer require --dev inpsyde/wp-tests-starter yoast/phpunit-polyfills wordpress/wordpress phpunit/phpunit
54 |
55 | Note that this will take a while as Composer will analyze the entire WordPress repository on GitHub. (Once a composer.lock is in place it will go faster on the next install run)
56 |
57 | ## Setup your tests
58 |
59 | To set up your PHPUnit tests you need two files in place: `phpunit.xml.dist` and a `boostrap.php` which gets loaded by
60 | PHPUnit before your actual tests are executed. The shown examples of these two files assume a directory structure of your
61 | library like this:
62 |
63 | ├ src/
64 | | └ MyModule.php
65 | ├ tests/
66 | | ├integration/
67 | | | └ MyModuleTest.php
68 | | └boostrap.php
69 | ├ vendor/
70 | ├ composer.json
71 | └ phpunit.xml.dist
72 |
73 | The following example of the phpunit.xml.dist file tells PHPUnit where the test files resides and contains the database
74 | credentials as an environment variable:
75 |
76 | ```xml
77 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | ./tests/integration
87 |
88 |
89 |
90 | ```
91 |
92 | The `tests/boostrap.php` finally loads Wp Tests Starter and WordPress:
93 |
94 | ```php
95 | testAsMultisite()
116 | // boostrap your plugin or module code
117 | ->addActivePlugin(static function() {
118 | (new MyModule())->init();
119 | })
120 | // add filters early
121 | ->addFilter('my_app.modules', static function(array $modules): array {
122 | // whatever, it's just an example
123 | return $modules;
124 | })
125 | //finally load WordPress
126 | ->bootstrap();
127 | ```
128 |
129 | These files just show a short way to run integration tests with WP Tests Starter. You have several other configuration
130 | options available through the methods of the `WpTestsStarter` object.
131 |
132 | ## Run PHPUnit
133 |
134 | With this configuration in place you can run PHPUnit to execute all test classes in `tests/integration` with
135 |
136 | vendor/bin/phpunit
137 |
138 | On every run, WP Starter will write the configuration to `vendor/wordpress/wordpress/wp-config.php` and load the WordPress
139 | internal bootstrap script which ensures installation of the database tables for example.
140 |
141 | ## Configuration
142 |
143 | ### DB Url
144 |
145 | In order to not have to maintain several environment variables you can pass all databse credentials and options via a
146 | single parameter like this:
147 |
148 | mysql://user:password@localhost:3306/test_db?table_prefix=wp_tests_&charset=utf8mb4&collation=utf8_general_ci
149 |
150 | This URL can be passed to `WpTestsStarter` either by constructor parameter or by `useDbUrl()` method:
151 |
152 | ```php
153 | useDbUrl($dbUrl);
158 | ```
159 |
160 | The URL example above would turn into the following WordPress constants and globals:
161 |
162 | ```php
163 | useDbHost('localhost')
179 | ->useDbUser('user');
180 | // and so on
181 | ```
182 |
183 | ## Copyright and License
184 |
185 | This package is [free software](https://www.gnu.org/philosophy/free-sw.en.html) distributed under the terms of the GNU General Public License version 2 or (at your option) any later version. For the full license, see [LICENSE](./LICENSE).
186 |
187 |
--------------------------------------------------------------------------------
/.ddev/config.yaml:
--------------------------------------------------------------------------------
1 | name: wp-tests-starter
2 | type: php
3 | docroot: public
4 | php_version: "7.4"
5 | webserver_type: nginx-fpm
6 | router_http_port: "80"
7 | router_https_port: "443"
8 | xdebug_enabled: true
9 | additional_hostnames: []
10 | additional_fqdns: []
11 | database:
12 | type: mariadb
13 | version: "10.4"
14 | nfs_mount_enabled: false
15 | mutagen_enabled: false
16 | project_tld: localhost
17 | use_dns_when_possible: true
18 | composer_version: "2"
19 | web_environment: []
20 | nodejs_version: "16"
21 |
22 | # Key features of ddev's config.yaml:
23 |
24 | # name: # Name of the project, automatically provides
25 | # http://projectname.ddev.site and https://projectname.ddev.site
26 |
27 | # type: # drupal6/7/8, backdrop, typo3, wordpress, php
28 |
29 | # docroot: # Relative path to the directory containing index.php.
30 |
31 | # php_version: "7.4" # PHP version to use, "5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2"
32 |
33 | # You can explicitly specify the webimage but this
34 | # is not recommended, as the images are often closely tied to ddev's' behavior,
35 | # so this can break upgrades.
36 |
37 | # webimage: # nginx/php docker image.
38 |
39 | # database:
40 | # type: # mysql, mariadb
41 | # version: # database version, like "10.3" or "8.0"
42 | # Note that mariadb_version or mysql_version from v1.18 and earlier
43 | # will automatically be converted to this notation with just a "ddev config --auto"
44 |
45 | # router_http_port: # Port to be used for http (defaults to port 80)
46 | # router_https_port: # Port for https (defaults to 443)
47 |
48 | # xdebug_enabled: false # Set to true to enable xdebug and "ddev start" or "ddev restart"
49 | # Note that for most people the commands
50 | # "ddev xdebug" to enable xdebug and "ddev xdebug off" to disable it work better,
51 | # as leaving xdebug enabled all the time is a big performance hit.
52 |
53 | # xhprof_enabled: false # Set to true to enable xhprof and "ddev start" or "ddev restart"
54 | # Note that for most people the commands
55 | # "ddev xhprof" to enable xhprof and "ddev xhprof off" to disable it work better,
56 | # as leaving xhprof enabled all the time is a big performance hit.
57 |
58 | # webserver_type: nginx-fpm # or apache-fpm
59 |
60 | # timezone: Europe/Berlin
61 | # This is the timezone used in the containers and by PHP;
62 | # it can be set to any valid timezone,
63 | # see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
64 | # For example Europe/Dublin or MST7MDT
65 |
66 | # composer_root:
67 | # Relative path to the composer root directory from the project root. This is
68 | # the directory which contains the composer.json and where all Composer related
69 | # commands are executed.
70 |
71 | # composer_version: "2"
72 | # You can set it to "" or "2" (default) for Composer v2 or "1" for Composer v1
73 | # to use the latest major version available at the time your container is built.
74 | # It is also possible to use each other Composer version channel. This includes:
75 | # - 2.2 (latest Composer LTS version)
76 | # - stable
77 | # - preview
78 | # - snapshot
79 | # Alternatively, an explicit Composer version may be specified, for example "2.2.18".
80 | # To reinstall Composer after the image was built, run "ddev debug refresh".
81 |
82 | # nodejs_version: "16"
83 | # change from the default system Node.js version to another supported version, like 12, 14, 17, 18.
84 | # Note that you can use 'ddev nvm' or nvm inside the web container to provide nearly any
85 | # Node.js version, including v6, etc.
86 |
87 | # additional_hostnames:
88 | # - somename
89 | # - someothername
90 | # would provide http and https URLs for "somename.ddev.site"
91 | # and "someothername.ddev.site".
92 |
93 | # additional_fqdns:
94 | # - example.com
95 | # - sub1.example.com
96 | # would provide http and https URLs for "example.com" and "sub1.example.com"
97 | # Please take care with this because it can cause great confusion.
98 |
99 | # upload_dir: custom/upload/dir
100 | # would set the destination path for ddev import-files to /custom/upload/dir
101 | # When mutagen is enabled this path is bind-mounted so that all the files
102 | # in the upload_dir don't have to be synced into mutagen
103 |
104 | # working_dir:
105 | # web: /var/www/html
106 | # db: /home
107 | # would set the default working directory for the web and db services.
108 | # These values specify the destination directory for ddev ssh and the
109 | # directory in which commands passed into ddev exec are run.
110 |
111 | # omit_containers: [db, dba, ddev-ssh-agent]
112 | # Currently only these containers are supported. Some containers can also be
113 | # omitted globally in the ~/.ddev/global_config.yaml. Note that if you omit
114 | # the "db" container, several standard features of ddev that access the
115 | # database container will be unusable. In the global configuration it is also
116 | # possible to omit ddev-router, but not here.
117 |
118 | # nfs_mount_enabled: false
119 | # Great performance improvement but requires host configuration first.
120 | # See https://ddev.readthedocs.io/en/latest/users/install/performance/#nfs
121 |
122 | # mutagen_enabled: false
123 | # Performance improvement using mutagen asynchronous updates.
124 | # See https://ddev.readthedocs.io/en/latest/users/install/performance/#mutagen
125 |
126 | # fail_on_hook_fail: False
127 | # Decide whether 'ddev start' should be interrupted by a failing hook
128 |
129 | # host_https_port: "59002"
130 | # The host port binding for https can be explicitly specified. It is
131 | # dynamic unless otherwise specified.
132 | # This is not used by most people, most people use the *router* instead
133 | # of the localhost port.
134 |
135 | # host_webserver_port: "59001"
136 | # The host port binding for the ddev-webserver can be explicitly specified. It is
137 | # dynamic unless otherwise specified.
138 | # This is not used by most people, most people use the *router* instead
139 | # of the localhost port.
140 |
141 | # host_db_port: "59002"
142 | # The host port binding for the ddev-dbserver can be explicitly specified. It is dynamic
143 | # unless explicitly specified.
144 |
145 | # phpmyadmin_port: "8036"
146 | # phpmyadmin_https_port: "8037"
147 | # The PHPMyAdmin ports can be changed from the default 8036 and 8037
148 |
149 | # host_phpmyadmin_port: "8036"
150 | # The phpmyadmin (dba) port is not normally bound on the host at all, instead being routed
151 | # through ddev-router, but it can be specified and bound.
152 |
153 | # mailhog_port: "8025"
154 | # mailhog_https_port: "8026"
155 | # The MailHog ports can be changed from the default 8025 and 8026
156 |
157 | # host_mailhog_port: "8025"
158 | # The mailhog port is not normally bound on the host at all, instead being routed
159 | # through ddev-router, but it can be bound directly to localhost if specified here.
160 |
161 | # webimage_extra_packages: [php7.4-tidy, php-bcmath]
162 | # Extra Debian packages that are needed in the webimage can be added here
163 |
164 | # dbimage_extra_packages: [telnet,netcat]
165 | # Extra Debian packages that are needed in the dbimage can be added here
166 |
167 | # use_dns_when_possible: true
168 | # If the host has internet access and the domain configured can
169 | # successfully be looked up, DNS will be used for hostname resolution
170 | # instead of editing /etc/hosts
171 | # Defaults to true
172 |
173 | # project_tld: ddev.site
174 | # The top-level domain used for project URLs
175 | # The default "ddev.site" allows DNS lookup via a wildcard
176 | # If you prefer you can change this to "ddev.local" to preserve
177 | # pre-v1.9 behavior.
178 |
179 | # ngrok_args: --basic-auth username:pass1234
180 | # Provide extra flags to the "ngrok http" command, see
181 | # https://ngrok.com/docs#http or run "ngrok http -h"
182 |
183 | # disable_settings_management: false
184 | # If true, ddev will not create CMS-specific settings files like
185 | # Drupal's settings.php/settings.ddev.php or TYPO3's AdditionalConfiguration.php
186 | # In this case the user must provide all such settings.
187 |
188 | # You can inject environment variables into the web container with:
189 | # web_environment:
190 | # - SOMEENV=somevalue
191 | # - SOMEOTHERENV=someothervalue
192 |
193 | # no_project_mount: false
194 | # (Experimental) If true, ddev will not mount the project into the web container;
195 | # the user is responsible for mounting it manually or via a script.
196 | # This is to enable experimentation with alternate file mounting strategies.
197 | # For advanced users only!
198 |
199 | # bind_all_interfaces: false
200 | # If true, host ports will be bound on all network interfaces,
201 | # not just the localhost interface. This means that ports
202 | # will be available on the local network if the host firewall
203 | # allows it.
204 |
205 | # default_container_timeout: 120
206 | # The default time that ddev waits for all containers to become ready can be increased from
207 | # the default 120. This helps in importing huge databases, for example.
208 |
209 | #web_extra_exposed_ports:
210 | #- name: nodejs
211 | # container_port: 3000
212 | # http_port: 2999
213 | # https_port: 3000
214 | #- name: something
215 | # container_port: 4000
216 | # https_port: 4000
217 | # http_port: 3999
218 | # Allows a set of extra ports to be exposed via ddev-router
219 | # The port behavior on the ddev-webserver must be arranged separately, for example
220 | # using web_extra_daemons.
221 | # For example, with a web app on port 3000 inside the container, this config would
222 | # expose that web app on https://.ddev.site:9999 and http://.ddev.site:9998
223 | # web_extra_exposed_ports:
224 | # - container_port: 3000
225 | # http_port: 9998
226 | # https_port: 9999
227 |
228 | #web_extra_daemons:
229 | #- name: "http-1"
230 | # command: "/var/www/html/node_modules/.bin/http-server -p 3000"
231 | # directory: /var/www/html
232 | #- name: "http-2"
233 | # command: "/var/www/html/node_modules/.bin/http-server /var/www/html/sub -p 3000"
234 | # directory: /var/www/html
235 |
236 | # override_config: false
237 | # By default, config.*.yaml files are *merged* into the configuration
238 | # But this means that some things can't be overridden
239 | # For example, if you have 'nfs_mount_enabled: true'' you can't override it with a merge
240 | # and you can't erase existing hooks or all environment variables.
241 | # However, with "override_config: true" in a particular config.*.yaml file,
242 | # 'nfs_mount_enabled: false' can override the existing values, and
243 | # hooks:
244 | # post-start: []
245 | # or
246 | # web_environment: []
247 | # or
248 | # additional_hostnames: []
249 | # can have their intended affect. 'override_config' affects only behavior of the
250 | # config.*.yaml file it exists in.
251 |
252 | # Many ddev commands can be extended to run tasks before or after the
253 | # ddev command is executed, for example "post-start", "post-import-db",
254 | # "pre-composer", "post-composer"
255 | # See https://ddev.readthedocs.io/en/stable/users/extend/custom-commands/ for more
256 | # information on the commands that can be extended and the tasks you can define
257 | # for them. Example:
258 | #hooks:
259 |
--------------------------------------------------------------------------------
/src/WpTestsStarter.php:
--------------------------------------------------------------------------------
1 | baseDir = rtrim($baseDir, '\\/');
40 | $this->saltGenerator = $saltGenerator ?? new SaltGenerator();
41 | $this->dbUrlParser = $dbUrlParser ?? new DbUrlParser();
42 |
43 | // set some common defaults
44 | $this->useDbHost('localhost')
45 | ->useSiteDomain('example.tld')
46 | ->useEmail('admin@example.tld')
47 | ->useSiteTitle('Wp Tests Starter')
48 | ->usePhpBinary('/usr/bin/php')
49 | ->useTablePrefix('wp_tests_')
50 | ->useAbsPath($this->baseDir . '/src/')
51 | ->generateSalts();
52 |
53 | $dbUrl and $this->useDbUrl($dbUrl);
54 | }
55 |
56 | /**
57 | * Loading the WordPress testing bootstrap
58 | */
59 | public function bootstrap()
60 | {
61 | $this->defineConstants();
62 | $this->declareGlobals();
63 | $this->writeConfigFile();
64 | $this->installLivePlugin();
65 |
66 | $wpBoostrapFile = $this->baseDir . '/tests/phpunit/includes/bootstrap.php';
67 | require_once $wpBoostrapFile;
68 | }
69 |
70 | public function useConst(string $const, $value): self
71 | {
72 | $this->constants[$const] = $value;
73 |
74 | return $this;
75 | }
76 |
77 | public function useGlobalVar(string $var, $value): self
78 | {
79 | $this->globalsFactories[] = static function () use ($var, $value): void {
80 | $GLOBALS[$var] = $value;
81 | };
82 |
83 | return $this;
84 | }
85 |
86 | public function useDbUrl(string $dbUrl): self
87 | {
88 | $credentials = $this->dbUrlParser->parse($dbUrl);
89 |
90 | $credentials['host'] and $this->useDbHost($credentials['host']);
91 | $credentials['user'] and $this->useDbUser($credentials['user']);
92 | $credentials['password'] and $this->useDbPassword($credentials['password']);
93 | $credentials['db'] and $this->useDbName($credentials['db']);
94 | $credentials['table_prefix'] and $this->useTablePrefix($credentials['table_prefix']);
95 | $credentials['charset'] and $this->useDbCharset($credentials['charset']);
96 | $credentials['collation'] and $this->useDbCollation($credentials['collation']);
97 |
98 | return $this;
99 | }
100 |
101 | public function useAbsPath(?string $abspath = null): self
102 | {
103 | return $this->useConst('ABSPATH', $abspath);
104 | }
105 |
106 | public function useDbName(string $dbName): self
107 | {
108 | return $this->useConst('DB_NAME', $dbName);
109 | }
110 |
111 | public function useDbHost(string $dbHost): self
112 | {
113 | return $this->useConst('DB_HOST', $dbHost);
114 | }
115 |
116 | public function useDbUser(string $dbUser): self
117 | {
118 | return $this->useConst('DB_USER', $dbUser);
119 | }
120 |
121 | public function useDbPassword(string $dbPassword): self
122 | {
123 | return $this->useConst('DB_PASSWORD', $dbPassword);
124 | }
125 |
126 | public function useDbCharset(string $dbCharset): self
127 | {
128 | return $this->useConst('DB_CHARSET', $dbCharset);
129 | }
130 |
131 | public function useDbCollation(string $dbCollation): self
132 | {
133 | return $this->useConst('DB_COLLATE', $dbCollation);
134 | }
135 |
136 | public function useDebugMode(bool $wpDebug): self
137 | {
138 | return $this->useConst('WP_DEBUG', $wpDebug);
139 | }
140 |
141 | public function useSiteDomain(string $domain): self
142 | {
143 | return $this->useConst('WP_TESTS_DOMAIN', $domain);
144 | }
145 |
146 | public function useEmail(string $email): self
147 | {
148 | return $this->useConst('WP_TESTS_EMAIL', $email);
149 | }
150 |
151 | public function useSiteTitle(string $title): self
152 | {
153 | return $this->useConst('WP_TESTS_TITLE', $title);
154 | }
155 |
156 | public function usePhpBinary(string $binary): self
157 | {
158 | return $this->useConst('WP_PHP_BINARY', $binary);
159 | }
160 |
161 | public function testAsMultisite(bool $isMultisite = true): self
162 | {
163 | return $this->useConst('WP_TESTS_MULTISITE', $isMultisite);
164 | }
165 |
166 | public function useWpPluginDir(string $dir): self
167 | {
168 | $dir = rtrim($dir, '\\/');
169 |
170 | return $this->useConst('WP_PLUGIN_DIR', $dir);
171 | }
172 |
173 | /**
174 | * add action before WP core is loaded
175 | */
176 | public function addAction(string $action, callable $listener, int $priority): self
177 | {
178 | return $this->addFilter($action, $listener, $priority);
179 | }
180 |
181 | /**
182 | * add filter before WP core is loaded
183 | */
184 | public function addFilter(string $filter, callable $listener, int $priority = 10): self
185 | {
186 | isset($GLOBALS['wp_filter']) or $GLOBALS['wp_filter'] = [];
187 | isset($GLOBALS['wp_filter'][$filter]) or $GLOBALS['wp_filter'][$filter] = [];
188 | isset($GLOBALS['wp_filter'][$filter][$priority]) or $GLOBALS['wp_filter'][$filter][$priority] = [];
189 |
190 | $GLOBALS['wp_filter'][$filter][$priority][] = [
191 | 'accepted_args' => PHP_INT_MAX,
192 | 'function' => $listener
193 | ];
194 |
195 | return $this;
196 | }
197 |
198 | /**
199 | * @param string $plugin a plugin file relative to WP's plugin directory like 'directory/plugin-file.php'
200 | */
201 | public function addActivePlugin(string $plugin): self
202 | {
203 | $this->globalsFactories[] = static function () use ($plugin): void {
204 | if (! isset($GLOBALS['wp_tests_options'])) {
205 | $GLOBALS['wp_tests_options'] = [];
206 | }
207 |
208 | if (! isset($GLOBALS['wp_tests_options']['active_plugins'])) {
209 | $GLOBALS['wp_tests_options']['active_plugins'] = [];
210 | }
211 |
212 | if (in_array($plugin, $GLOBALS['wp_tests_options']['active_plugins'])) {
213 | return;
214 | }
215 |
216 | $GLOBALS['wp_tests_options']['active_plugins'][] = $plugin;
217 | };
218 |
219 | return $this;
220 | }
221 |
222 | public function addLivePlugin(callable $plugin): self
223 | {
224 | self::$livePlugins[] = $plugin;
225 |
226 | return $this;
227 | }
228 |
229 | public function useTablePrefix(string $prefix): self
230 | {
231 | return $this->useGlobalVar('table_prefix', $prefix);
232 | }
233 |
234 | public static function runLivePlugins(): void
235 | {
236 | foreach(self::$livePlugins as $livePlugin) {
237 | $livePlugin();
238 | }
239 | }
240 |
241 | private function installLivePlugin(): void
242 | {
243 | if(!self::$livePlugins) {
244 | return;
245 | }
246 |
247 | $pluginCode = <<<'PHP'
248 | muPluginDir();
268 | if(!is_dir($muPluginDir)) {
269 | mkdir($muPluginDir, 0755, true);
270 | }
271 |
272 | $pluginFile = $muPluginDir . '/wp-tests-starter-live-plugin.php';
273 | file_put_contents(
274 | $pluginFile,
275 | $pluginCode
276 | );
277 | }
278 |
279 | private function defineConstants(): void
280 | {
281 | foreach ($this->constants as $constant => $value) {
282 | if (defined($constant)) {
283 | continue;
284 | }
285 |
286 | define($constant, $value);
287 | }
288 | }
289 |
290 | private function declareGlobals(): void
291 | {
292 | foreach ($this->globalsFactories as $factory) {
293 | $factory();
294 | }
295 | }
296 |
297 | private function generateSalts()
298 | {
299 | $saltConstants = [
300 | 'AUTH_KEY',
301 | 'SECURE_AUTH_KEY',
302 | 'LOGGED_IN_KEY',
303 | 'NONCE_KEY',
304 | 'SECURE_AUTH_SALT',
305 | 'LOGGED_IN_SALT',
306 | 'NONCE_SALT',
307 | 'AUTH_KEY',
308 | ];
309 |
310 | foreach ($saltConstants as $constant) {
311 | $this->useConst(
312 | $constant,
313 | $this->saltGenerator->generateSalt()
314 | );
315 | }
316 | }
317 |
318 | private function muPluginDir(): ?string
319 | {
320 | if(defined('WPMU_PLUGIN_DIR')) {
321 | return (string) WPMU_PLUGIN_DIR;
322 | }
323 |
324 | if(array_key_exists('WPMU_PLUGIN_DIR', $this->constants)) {
325 | return (string) $this->constants['WPMU_PLUGIN_DIR'];
326 | }
327 |
328 | if(defined('WP_CONTENT_DIR')) {
329 | return WP_CONTENT_DIR . '/mu-plugins';
330 | }
331 |
332 | if(array_key_exists('WP_CONTENT_DIR', $this->constants)) {
333 | return $this->constants['WP_CONTENT_DIR'] . '/mu-plugins';
334 | }
335 |
336 | return $this->baseDir . '/src/wp-content/mu-plugins';
337 | }
338 |
339 | private function writeConfigFile()
340 | {
341 | $configFile = $this->getConfigFile();
342 | if (! file_exists($configFile)) {
343 | touch($configFile);
344 | }
345 |
346 | /**
347 | * We have to persist all dynamic configuration (constants and globals) in a wp-config.php
348 | * as the WordPress internal boostrap process runs a sub process for the installation (setup DB tables)
349 | */
350 | $constantsDefinition = $this->getDefinedConstantsCode();
351 | $content = <<baseDir . '/wp-tests-config.php';
363 | }
364 |
365 | private function getDefinedConstantsCode(): string
366 | {
367 | $code = '';
368 | foreach ($this->constants as $constant => $value) {
369 | $constant = $this->escapePhpString($constant);
370 | $value = $this->escapePhpString($value);
371 | $code .= "if ( ! defined( '{$constant}' ) )\n";
372 | $code .= "\tdefine( '{$constant}', '{$value}' );\n";
373 | }
374 |
375 | return $code;
376 | }
377 |
378 | private function escapePhpString($value): string
379 | {
380 | $value = str_replace(
381 | [''],
382 | '',
383 | (string) $value
384 | );
385 | $value = addcslashes($value, "'\\");
386 |
387 | return $value;
388 | }
389 | }
390 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | ### GNU GENERAL PUBLIC LICENSE
2 |
3 | Version 2, June 1991
4 |
5 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.
6 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
7 |
8 | Everyone is permitted to copy and distribute verbatim copies
9 | of this license document, but changing it is not allowed.
10 |
11 | ### Preamble
12 |
13 | The licenses for most software are designed to take away your freedom
14 | to share and change it. By contrast, the GNU General Public License is
15 | intended to guarantee your freedom to share and change free
16 | software--to make sure the software is free for all its users. This
17 | General Public License applies to most of the Free Software
18 | Foundation's software and to any other program whose authors commit to
19 | using it. (Some other Free Software Foundation software is covered by
20 | the GNU Lesser General Public License instead.) You can apply it to
21 | your programs, too.
22 |
23 | When we speak of free software, we are referring to freedom, not
24 | price. Our General Public Licenses are designed to make sure that you
25 | have the freedom to distribute copies of free software (and charge for
26 | this service if you wish), that you receive source code or can get it
27 | if you want it, that you can change the software or use pieces of it
28 | in new free programs; and that you know you can do these things.
29 |
30 | To protect your rights, we need to make restrictions that forbid
31 | anyone to deny you these rights or to ask you to surrender the rights.
32 | These restrictions translate to certain responsibilities for you if
33 | you distribute copies of the software, or if you modify it.
34 |
35 | For example, if you distribute copies of such a program, whether
36 | gratis or for a fee, you must give the recipients all the rights that
37 | you have. You must make sure that they, too, receive or can get the
38 | source code. And you must show them these terms so they know their
39 | rights.
40 |
41 | We protect your rights with two steps: (1) copyright the software, and
42 | (2) offer you this license which gives you legal permission to copy,
43 | distribute and/or modify the software.
44 |
45 | Also, for each author's protection and ours, we want to make certain
46 | that everyone understands that there is no warranty for this free
47 | software. If the software is modified by someone else and passed on,
48 | we want its recipients to know that what they have is not the
49 | original, so that any problems introduced by others will not reflect
50 | on the original authors' reputations.
51 |
52 | Finally, any free program is threatened constantly by software
53 | patents. We wish to avoid the danger that redistributors of a free
54 | program will individually obtain patent licenses, in effect making the
55 | program proprietary. To prevent this, we have made it clear that any
56 | patent must be licensed for everyone's free use or not licensed at
57 | all.
58 |
59 | The precise terms and conditions for copying, distribution and
60 | modification follow.
61 |
62 | ### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
63 |
64 | **0.** This License applies to any program or other work which
65 | contains a notice placed by the copyright holder saying it may be
66 | distributed under the terms of this General Public License. The
67 | "Program", below, refers to any such program or work, and a "work
68 | based on the Program" means either the Program or any derivative work
69 | under copyright law: that is to say, a work containing the Program or
70 | a portion of it, either verbatim or with modifications and/or
71 | translated into another language. (Hereinafter, translation is
72 | included without limitation in the term "modification".) Each licensee
73 | is addressed as "you".
74 |
75 | Activities other than copying, distribution and modification are not
76 | covered by this License; they are outside its scope. The act of
77 | running the Program is not restricted, and the output from the Program
78 | is covered only if its contents constitute a work based on the Program
79 | (independent of having been made by running the Program). Whether that
80 | is true depends on what the Program does.
81 |
82 | **1.** You may copy and distribute verbatim copies of the Program's
83 | source code as you receive it, in any medium, provided that you
84 | conspicuously and appropriately publish on each copy an appropriate
85 | copyright notice and disclaimer of warranty; keep intact all the
86 | notices that refer to this License and to the absence of any warranty;
87 | and give any other recipients of the Program a copy of this License
88 | along with the Program.
89 |
90 | You may charge a fee for the physical act of transferring a copy, and
91 | you may at your option offer warranty protection in exchange for a
92 | fee.
93 |
94 | **2.** You may modify your copy or copies of the Program or any
95 | portion of it, thus forming a work based on the Program, and copy and
96 | distribute such modifications or work under the terms of Section 1
97 | above, provided that you also meet all of these conditions:
98 |
99 |
100 | **a)** You must cause the modified files to carry prominent notices
101 | stating that you changed the files and the date of any change.
102 |
103 |
104 | **b)** You must cause any work that you distribute or publish, that in
105 | whole or in part contains or is derived from the Program or any part
106 | thereof, to be licensed as a whole at no charge to all third parties
107 | under the terms of this License.
108 |
109 |
110 | **c)** If the modified program normally reads commands interactively
111 | when run, you must cause it, when started running for such interactive
112 | use in the most ordinary way, to print or display an announcement
113 | including an appropriate copyright notice and a notice that there is
114 | no warranty (or else, saying that you provide a warranty) and that
115 | users may redistribute the program under these conditions, and telling
116 | the user how to view a copy of this License. (Exception: if the
117 | Program itself is interactive but does not normally print such an
118 | announcement, your work based on the Program is not required to print
119 | an announcement.)
120 |
121 | These requirements apply to the modified work as a whole. If
122 | identifiable sections of that work are not derived from the Program,
123 | and can be reasonably considered independent and separate works in
124 | themselves, then this License, and its terms, do not apply to those
125 | sections when you distribute them as separate works. But when you
126 | distribute the same sections as part of a whole which is a work based
127 | on the Program, the distribution of the whole must be on the terms of
128 | this License, whose permissions for other licensees extend to the
129 | entire whole, and thus to each and every part regardless of who wrote
130 | it.
131 |
132 | Thus, it is not the intent of this section to claim rights or contest
133 | your rights to work written entirely by you; rather, the intent is to
134 | exercise the right to control the distribution of derivative or
135 | collective works based on the Program.
136 |
137 | In addition, mere aggregation of another work not based on the Program
138 | with the Program (or with a work based on the Program) on a volume of
139 | a storage or distribution medium does not bring the other work under
140 | the scope of this License.
141 |
142 | **3.** You may copy and distribute the Program (or a work based on it,
143 | under Section 2) in object code or executable form under the terms of
144 | Sections 1 and 2 above provided that you also do one of the following:
145 |
146 |
147 | **a)** Accompany it with the complete corresponding machine-readable
148 | source code, which must be distributed under the terms of Sections 1
149 | and 2 above on a medium customarily used for software interchange; or,
150 |
151 |
152 | **b)** Accompany it with a written offer, valid for at least three
153 | years, to give any third party, for a charge no more than your cost of
154 | physically performing source distribution, a complete machine-readable
155 | copy of the corresponding source code, to be distributed under the
156 | terms of Sections 1 and 2 above on a medium customarily used for
157 | software interchange; or,
158 |
159 |
160 | **c)** Accompany it with the information you received as to the offer
161 | to distribute corresponding source code. (This alternative is allowed
162 | only for noncommercial distribution and only if you received the
163 | program in object code or executable form with such an offer, in
164 | accord with Subsection b above.)
165 |
166 | The source code for a work means the preferred form of the work for
167 | making modifications to it. For an executable work, complete source
168 | code means all the source code for all modules it contains, plus any
169 | associated interface definition files, plus the scripts used to
170 | control compilation and installation of the executable. However, as a
171 | special exception, the source code distributed need not include
172 | anything that is normally distributed (in either source or binary
173 | form) with the major components (compiler, kernel, and so on) of the
174 | operating system on which the executable runs, unless that component
175 | itself accompanies the executable.
176 |
177 | If distribution of executable or object code is made by offering
178 | access to copy from a designated place, then offering equivalent
179 | access to copy the source code from the same place counts as
180 | distribution of the source code, even though third parties are not
181 | compelled to copy the source along with the object code.
182 |
183 | **4.** You may not copy, modify, sublicense, or distribute the Program
184 | except as expressly provided under this License. Any attempt otherwise
185 | to copy, modify, sublicense or distribute the Program is void, and
186 | will automatically terminate your rights under this License. However,
187 | parties who have received copies, or rights, from you under this
188 | License will not have their licenses terminated so long as such
189 | parties remain in full compliance.
190 |
191 | **5.** You are not required to accept this License, since you have not
192 | signed it. However, nothing else grants you permission to modify or
193 | distribute the Program or its derivative works. These actions are
194 | prohibited by law if you do not accept this License. Therefore, by
195 | modifying or distributing the Program (or any work based on the
196 | Program), you indicate your acceptance of this License to do so, and
197 | all its terms and conditions for copying, distributing or modifying
198 | the Program or works based on it.
199 |
200 | **6.** Each time you redistribute the Program (or any work based on
201 | the Program), the recipient automatically receives a license from the
202 | original licensor to copy, distribute or modify the Program subject to
203 | these terms and conditions. You may not impose any further
204 | restrictions on the recipients' exercise of the rights granted herein.
205 | You are not responsible for enforcing compliance by third parties to
206 | this License.
207 |
208 | **7.** If, as a consequence of a court judgment or allegation of
209 | patent infringement or for any other reason (not limited to patent
210 | issues), conditions are imposed on you (whether by court order,
211 | agreement or otherwise) that contradict the conditions of this
212 | License, they do not excuse you from the conditions of this License.
213 | If you cannot distribute so as to satisfy simultaneously your
214 | obligations under this License and any other pertinent obligations,
215 | then as a consequence you may not distribute the Program at all. For
216 | example, if a patent license would not permit royalty-free
217 | redistribution of the Program by all those who receive copies directly
218 | or indirectly through you, then the only way you could satisfy both it
219 | and this License would be to refrain entirely from distribution of the
220 | Program.
221 |
222 | If any portion of this section is held invalid or unenforceable under
223 | any particular circumstance, the balance of the section is intended to
224 | apply and the section as a whole is intended to apply in other
225 | circumstances.
226 |
227 | It is not the purpose of this section to induce you to infringe any
228 | patents or other property right claims or to contest validity of any
229 | such claims; this section has the sole purpose of protecting the
230 | integrity of the free software distribution system, which is
231 | implemented by public license practices. Many people have made
232 | generous contributions to the wide range of software distributed
233 | through that system in reliance on consistent application of that
234 | system; it is up to the author/donor to decide if he or she is willing
235 | to distribute software through any other system and a licensee cannot
236 | impose that choice.
237 |
238 | This section is intended to make thoroughly clear what is believed to
239 | be a consequence of the rest of this License.
240 |
241 | **8.** If the distribution and/or use of the Program is restricted in
242 | certain countries either by patents or by copyrighted interfaces, the
243 | original copyright holder who places the Program under this License
244 | may add an explicit geographical distribution limitation excluding
245 | those countries, so that distribution is permitted only in or among
246 | countries not thus excluded. In such case, this License incorporates
247 | the limitation as if written in the body of this License.
248 |
249 | **9.** The Free Software Foundation may publish revised and/or new
250 | versions of the General Public License from time to time. Such new
251 | versions will be similar in spirit to the present version, but may
252 | differ in detail to address new problems or concerns.
253 |
254 | Each version is given a distinguishing version number. If the Program
255 | specifies a version number of this License which applies to it and
256 | "any later version", you have the option of following the terms and
257 | conditions either of that version or of any later version published by
258 | the Free Software Foundation. If the Program does not specify a
259 | version number of this License, you may choose any version ever
260 | published by the Free Software Foundation.
261 |
262 | **10.** If you wish to incorporate parts of the Program into other
263 | free programs whose distribution conditions are different, write to
264 | the author to ask for permission. For software which is copyrighted by
265 | the Free Software Foundation, write to the Free Software Foundation;
266 | we sometimes make exceptions for this. Our decision will be guided by
267 | the two goals of preserving the free status of all derivatives of our
268 | free software and of promoting the sharing and reuse of software
269 | generally.
270 |
271 | **NO WARRANTY**
272 |
273 | **11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
274 | WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
275 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
276 | OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
277 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
278 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
279 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
280 | PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
281 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
282 |
283 | **12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
284 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
285 | AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU
286 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
287 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
288 | PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
289 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
290 | FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
291 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
292 | DAMAGES.
293 |
294 | ### END OF TERMS AND CONDITIONS
295 |
296 | ### How to Apply These Terms to Your New Programs
297 |
298 | If you develop a new program, and you want it to be of the greatest
299 | possible use to the public, the best way to achieve this is to make it
300 | free software which everyone can redistribute and change under these
301 | terms.
302 |
303 | To do so, attach the following notices to the program. It is safest to
304 | attach them to the start of each source file to most effectively
305 | convey the exclusion of warranty; and each file should have at least
306 | the "copyright" line and a pointer to where the full notice is found.
307 |
308 | one line to give the program's name and an idea of what it does.
309 | Copyright (C) yyyy name of author
310 |
311 | This program is free software; you can redistribute it and/or
312 | modify it under the terms of the GNU General Public License
313 | as published by the Free Software Foundation; either version 2
314 | of the License, or (at your option) any later version.
315 |
316 | This program is distributed in the hope that it will be useful,
317 | but WITHOUT ANY WARRANTY; without even the implied warranty of
318 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
319 | GNU General Public License for more details.
320 |
321 | You should have received a copy of the GNU General Public License
322 | along with this program; if not, write to the Free Software
323 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
324 |
325 | Also add information on how to contact you by electronic and paper
326 | mail.
327 |
328 | If the program is interactive, make it output a short notice like this
329 | when it starts in an interactive mode:
330 |
331 | Gnomovision version 69, Copyright (C) year name of author
332 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
333 | type `show w'. This is free software, and you are welcome
334 | to redistribute it under certain conditions; type `show c'
335 | for details.
336 |
337 | The hypothetical commands \`show w' and \`show c' should show the
338 | appropriate parts of the General Public License. Of course, the
339 | commands you use may be called something other than \`show w' and
340 | \`show c'; they could even be mouse-clicks or menu items--whatever
341 | suits your program.
342 |
343 | You should also get your employer (if you work as a programmer) or
344 | your school, if any, to sign a "copyright disclaimer" for the program,
345 | if necessary. Here is a sample; alter the names:
346 |
347 | Yoyodyne, Inc., hereby disclaims all copyright
348 | interest in the program `Gnomovision'
349 | (which makes passes at compilers) written
350 | by James Hacker.
351 |
352 | signature of Ty Coon, 1 April 1989
353 | Ty Coon, President of Vice
354 |
355 | This General Public License does not permit incorporating your program
356 | into proprietary programs. If your program is a subroutine library,
357 | you may consider it more useful to permit linking proprietary
358 | applications with the library. If this is what you want to do, use the
359 | [GNU Lesser General Public
360 | License](https://www.gnu.org/licenses/lgpl.html) instead of this
361 | License.### GNU GENERAL PUBLIC LICENSE
362 |
363 | Version 2, June 1991
364 |
365 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.
366 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
367 |
368 | Everyone is permitted to copy and distribute verbatim copies
369 | of this license document, but changing it is not allowed.
370 |
371 | ### Preamble
372 |
373 | The licenses for most software are designed to take away your freedom
374 | to share and change it. By contrast, the GNU General Public License is
375 | intended to guarantee your freedom to share and change free
376 | software--to make sure the software is free for all its users. This
377 | General Public License applies to most of the Free Software
378 | Foundation's software and to any other program whose authors commit to
379 | using it. (Some other Free Software Foundation software is covered by
380 | the GNU Lesser General Public License instead.) You can apply it to
381 | your programs, too.
382 |
383 | When we speak of free software, we are referring to freedom, not
384 | price. Our General Public Licenses are designed to make sure that you
385 | have the freedom to distribute copies of free software (and charge for
386 | this service if you wish), that you receive source code or can get it
387 | if you want it, that you can change the software or use pieces of it
388 | in new free programs; and that you know you can do these things.
389 |
390 | To protect your rights, we need to make restrictions that forbid
391 | anyone to deny you these rights or to ask you to surrender the rights.
392 | These restrictions translate to certain responsibilities for you if
393 | you distribute copies of the software, or if you modify it.
394 |
395 | For example, if you distribute copies of such a program, whether
396 | gratis or for a fee, you must give the recipients all the rights that
397 | you have. You must make sure that they, too, receive or can get the
398 | source code. And you must show them these terms so they know their
399 | rights.
400 |
401 | We protect your rights with two steps: (1) copyright the software, and
402 | (2) offer you this license which gives you legal permission to copy,
403 | distribute and/or modify the software.
404 |
405 | Also, for each author's protection and ours, we want to make certain
406 | that everyone understands that there is no warranty for this free
407 | software. If the software is modified by someone else and passed on,
408 | we want its recipients to know that what they have is not the
409 | original, so that any problems introduced by others will not reflect
410 | on the original authors' reputations.
411 |
412 | Finally, any free program is threatened constantly by software
413 | patents. We wish to avoid the danger that redistributors of a free
414 | program will individually obtain patent licenses, in effect making the
415 | program proprietary. To prevent this, we have made it clear that any
416 | patent must be licensed for everyone's free use or not licensed at
417 | all.
418 |
419 | The precise terms and conditions for copying, distribution and
420 | modification follow.
421 |
422 | ### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
423 |
424 | **0.** This License applies to any program or other work which
425 | contains a notice placed by the copyright holder saying it may be
426 | distributed under the terms of this General Public License. The
427 | "Program", below, refers to any such program or work, and a "work
428 | based on the Program" means either the Program or any derivative work
429 | under copyright law: that is to say, a work containing the Program or
430 | a portion of it, either verbatim or with modifications and/or
431 | translated into another language. (Hereinafter, translation is
432 | included without limitation in the term "modification".) Each licensee
433 | is addressed as "you".
434 |
435 | Activities other than copying, distribution and modification are not
436 | covered by this License; they are outside its scope. The act of
437 | running the Program is not restricted, and the output from the Program
438 | is covered only if its contents constitute a work based on the Program
439 | (independent of having been made by running the Program). Whether that
440 | is true depends on what the Program does.
441 |
442 | **1.** You may copy and distribute verbatim copies of the Program's
443 | source code as you receive it, in any medium, provided that you
444 | conspicuously and appropriately publish on each copy an appropriate
445 | copyright notice and disclaimer of warranty; keep intact all the
446 | notices that refer to this License and to the absence of any warranty;
447 | and give any other recipients of the Program a copy of this License
448 | along with the Program.
449 |
450 | You may charge a fee for the physical act of transferring a copy, and
451 | you may at your option offer warranty protection in exchange for a
452 | fee.
453 |
454 | **2.** You may modify your copy or copies of the Program or any
455 | portion of it, thus forming a work based on the Program, and copy and
456 | distribute such modifications or work under the terms of Section 1
457 | above, provided that you also meet all of these conditions:
458 |
459 |
460 | **a)** You must cause the modified files to carry prominent notices
461 | stating that you changed the files and the date of any change.
462 |
463 |
464 | **b)** You must cause any work that you distribute or publish, that in
465 | whole or in part contains or is derived from the Program or any part
466 | thereof, to be licensed as a whole at no charge to all third parties
467 | under the terms of this License.
468 |
469 |
470 | **c)** If the modified program normally reads commands interactively
471 | when run, you must cause it, when started running for such interactive
472 | use in the most ordinary way, to print or display an announcement
473 | including an appropriate copyright notice and a notice that there is
474 | no warranty (or else, saying that you provide a warranty) and that
475 | users may redistribute the program under these conditions, and telling
476 | the user how to view a copy of this License. (Exception: if the
477 | Program itself is interactive but does not normally print such an
478 | announcement, your work based on the Program is not required to print
479 | an announcement.)
480 |
481 | These requirements apply to the modified work as a whole. If
482 | identifiable sections of that work are not derived from the Program,
483 | and can be reasonably considered independent and separate works in
484 | themselves, then this License, and its terms, do not apply to those
485 | sections when you distribute them as separate works. But when you
486 | distribute the same sections as part of a whole which is a work based
487 | on the Program, the distribution of the whole must be on the terms of
488 | this License, whose permissions for other licensees extend to the
489 | entire whole, and thus to each and every part regardless of who wrote
490 | it.
491 |
492 | Thus, it is not the intent of this section to claim rights or contest
493 | your rights to work written entirely by you; rather, the intent is to
494 | exercise the right to control the distribution of derivative or
495 | collective works based on the Program.
496 |
497 | In addition, mere aggregation of another work not based on the Program
498 | with the Program (or with a work based on the Program) on a volume of
499 | a storage or distribution medium does not bring the other work under
500 | the scope of this License.
501 |
502 | **3.** You may copy and distribute the Program (or a work based on it,
503 | under Section 2) in object code or executable form under the terms of
504 | Sections 1 and 2 above provided that you also do one of the following:
505 |
506 |
507 | **a)** Accompany it with the complete corresponding machine-readable
508 | source code, which must be distributed under the terms of Sections 1
509 | and 2 above on a medium customarily used for software interchange; or,
510 |
511 |
512 | **b)** Accompany it with a written offer, valid for at least three
513 | years, to give any third party, for a charge no more than your cost of
514 | physically performing source distribution, a complete machine-readable
515 | copy of the corresponding source code, to be distributed under the
516 | terms of Sections 1 and 2 above on a medium customarily used for
517 | software interchange; or,
518 |
519 |
520 | **c)** Accompany it with the information you received as to the offer
521 | to distribute corresponding source code. (This alternative is allowed
522 | only for noncommercial distribution and only if you received the
523 | program in object code or executable form with such an offer, in
524 | accord with Subsection b above.)
525 |
526 | The source code for a work means the preferred form of the work for
527 | making modifications to it. For an executable work, complete source
528 | code means all the source code for all modules it contains, plus any
529 | associated interface definition files, plus the scripts used to
530 | control compilation and installation of the executable. However, as a
531 | special exception, the source code distributed need not include
532 | anything that is normally distributed (in either source or binary
533 | form) with the major components (compiler, kernel, and so on) of the
534 | operating system on which the executable runs, unless that component
535 | itself accompanies the executable.
536 |
537 | If distribution of executable or object code is made by offering
538 | access to copy from a designated place, then offering equivalent
539 | access to copy the source code from the same place counts as
540 | distribution of the source code, even though third parties are not
541 | compelled to copy the source along with the object code.
542 |
543 | **4.** You may not copy, modify, sublicense, or distribute the Program
544 | except as expressly provided under this License. Any attempt otherwise
545 | to copy, modify, sublicense or distribute the Program is void, and
546 | will automatically terminate your rights under this License. However,
547 | parties who have received copies, or rights, from you under this
548 | License will not have their licenses terminated so long as such
549 | parties remain in full compliance.
550 |
551 | **5.** You are not required to accept this License, since you have not
552 | signed it. However, nothing else grants you permission to modify or
553 | distribute the Program or its derivative works. These actions are
554 | prohibited by law if you do not accept this License. Therefore, by
555 | modifying or distributing the Program (or any work based on the
556 | Program), you indicate your acceptance of this License to do so, and
557 | all its terms and conditions for copying, distributing or modifying
558 | the Program or works based on it.
559 |
560 | **6.** Each time you redistribute the Program (or any work based on
561 | the Program), the recipient automatically receives a license from the
562 | original licensor to copy, distribute or modify the Program subject to
563 | these terms and conditions. You may not impose any further
564 | restrictions on the recipients' exercise of the rights granted herein.
565 | You are not responsible for enforcing compliance by third parties to
566 | this License.
567 |
568 | **7.** If, as a consequence of a court judgment or allegation of
569 | patent infringement or for any other reason (not limited to patent
570 | issues), conditions are imposed on you (whether by court order,
571 | agreement or otherwise) that contradict the conditions of this
572 | License, they do not excuse you from the conditions of this License.
573 | If you cannot distribute so as to satisfy simultaneously your
574 | obligations under this License and any other pertinent obligations,
575 | then as a consequence you may not distribute the Program at all. For
576 | example, if a patent license would not permit royalty-free
577 | redistribution of the Program by all those who receive copies directly
578 | or indirectly through you, then the only way you could satisfy both it
579 | and this License would be to refrain entirely from distribution of the
580 | Program.
581 |
582 | If any portion of this section is held invalid or unenforceable under
583 | any particular circumstance, the balance of the section is intended to
584 | apply and the section as a whole is intended to apply in other
585 | circumstances.
586 |
587 | It is not the purpose of this section to induce you to infringe any
588 | patents or other property right claims or to contest validity of any
589 | such claims; this section has the sole purpose of protecting the
590 | integrity of the free software distribution system, which is
591 | implemented by public license practices. Many people have made
592 | generous contributions to the wide range of software distributed
593 | through that system in reliance on consistent application of that
594 | system; it is up to the author/donor to decide if he or she is willing
595 | to distribute software through any other system and a licensee cannot
596 | impose that choice.
597 |
598 | This section is intended to make thoroughly clear what is believed to
599 | be a consequence of the rest of this License.
600 |
601 | **8.** If the distribution and/or use of the Program is restricted in
602 | certain countries either by patents or by copyrighted interfaces, the
603 | original copyright holder who places the Program under this License
604 | may add an explicit geographical distribution limitation excluding
605 | those countries, so that distribution is permitted only in or among
606 | countries not thus excluded. In such case, this License incorporates
607 | the limitation as if written in the body of this License.
608 |
609 | **9.** The Free Software Foundation may publish revised and/or new
610 | versions of the General Public License from time to time. Such new
611 | versions will be similar in spirit to the present version, but may
612 | differ in detail to address new problems or concerns.
613 |
614 | Each version is given a distinguishing version number. If the Program
615 | specifies a version number of this License which applies to it and
616 | "any later version", you have the option of following the terms and
617 | conditions either of that version or of any later version published by
618 | the Free Software Foundation. If the Program does not specify a
619 | version number of this License, you may choose any version ever
620 | published by the Free Software Foundation.
621 |
622 | **10.** If you wish to incorporate parts of the Program into other
623 | free programs whose distribution conditions are different, write to
624 | the author to ask for permission. For software which is copyrighted by
625 | the Free Software Foundation, write to the Free Software Foundation;
626 | we sometimes make exceptions for this. Our decision will be guided by
627 | the two goals of preserving the free status of all derivatives of our
628 | free software and of promoting the sharing and reuse of software
629 | generally.
630 |
631 | **NO WARRANTY**
632 |
633 | **11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
634 | WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
635 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
636 | OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
637 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
638 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
639 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
640 | PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
641 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
642 |
643 | **12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
644 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
645 | AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU
646 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
647 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
648 | PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
649 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
650 | FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
651 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
652 | DAMAGES.
653 |
654 | ### END OF TERMS AND CONDITIONS
655 |
656 | ### How to Apply These Terms to Your New Programs
657 |
658 | If you develop a new program, and you want it to be of the greatest
659 | possible use to the public, the best way to achieve this is to make it
660 | free software which everyone can redistribute and change under these
661 | terms.
662 |
663 | To do so, attach the following notices to the program. It is safest to
664 | attach them to the start of each source file to most effectively
665 | convey the exclusion of warranty; and each file should have at least
666 | the "copyright" line and a pointer to where the full notice is found.
667 |
668 | one line to give the program's name and an idea of what it does.
669 | Copyright (C) yyyy name of author
670 |
671 | This program is free software; you can redistribute it and/or
672 | modify it under the terms of the GNU General Public License
673 | as published by the Free Software Foundation; either version 2
674 | of the License, or (at your option) any later version.
675 |
676 | This program is distributed in the hope that it will be useful,
677 | but WITHOUT ANY WARRANTY; without even the implied warranty of
678 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
679 | GNU General Public License for more details.
680 |
681 | You should have received a copy of the GNU General Public License
682 | along with this program; if not, write to the Free Software
683 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
684 |
685 | Also add information on how to contact you by electronic and paper
686 | mail.
687 |
688 | If the program is interactive, make it output a short notice like this
689 | when it starts in an interactive mode:
690 |
691 | Gnomovision version 69, Copyright (C) year name of author
692 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
693 | type `show w'. This is free software, and you are welcome
694 | to redistribute it under certain conditions; type `show c'
695 | for details.
696 |
697 | The hypothetical commands \`show w' and \`show c' should show the
698 | appropriate parts of the General Public License. Of course, the
699 | commands you use may be called something other than \`show w' and
700 | \`show c'; they could even be mouse-clicks or menu items--whatever
701 | suits your program.
702 |
703 | You should also get your employer (if you work as a programmer) or
704 | your school, if any, to sign a "copyright disclaimer" for the program,
705 | if necessary. Here is a sample; alter the names:
706 |
707 | Yoyodyne, Inc., hereby disclaims all copyright
708 | interest in the program `Gnomovision'
709 | (which makes passes at compilers) written
710 | by James Hacker.
711 |
712 | signature of Ty Coon, 1 April 1989
713 | Ty Coon, President of Vice
714 |
715 | This General Public License does not permit incorporating your program
716 | into proprietary programs. If your program is a subroutine library,
717 | you may consider it more useful to permit linking proprietary
718 | applications with the library. If this is what you want to do, use the
719 | [GNU Lesser General Public
720 | License](https://www.gnu.org/licenses/lgpl.html) instead of this
721 | License.
722 |
--------------------------------------------------------------------------------