Giter Site home page Giter Site logo

kazurayam / materialstore Goto Github PK

View Code? Open in Web Editor NEW
0.0 1.0 0.0 59.01 MB

A domain-specific file system to store "materials" (screenshots, HTML, JSON, XML) collected during End-to-End testings using Selenium WebDriver etc. Features to make "diff" and compiling HTML reports are also included. This is written in pure Java8

License: Apache License 2.0

SCSS 0.34% CSS 0.14% JavaScript 2.16% Java 97.36%
selenium end-to-end-testing object-database java

materialstore's Introduction

materialstore's People

Contributors

kazurayam avatar

Watchers

 avatar

materialstore's Issues

add a method to delete old(unused) Materials in the Store

For example.

JobName jobName = new JobName("FooJob")
long amountToSubtract = 8
TemporalUnit unit = ChronoUnit.HOURS

int deletedObjectCount = store.deleteOldObjects(jobName, amountToSubtract, unit)

This should select all JobTimestamp directories in the directory of JobName "FooJob" which are older than now for 8 hours, then delete them recursively.

factory method of MetadataPattern that takes a Metadata object as source

I want to write:

            // store the JSON file into the Material directory
            Metadata metadata = new Metadata(param)
            store.write(jobName, jobTimestamp, FileType.JSON, metadata,
                    new JsonOutput().prettyPrint(rawJson))

            // retrieve the JSON file from the Material directory
            List<Material> materials = store.select(jobName, jobTimestamp,
                    MetadataPattern.create(metadata), FileType.JSON)
            assert materials.size() == 1
            

MetadataPattern.create(metadata) is not supported yet.

I need com.kazurayam.materialstore.Store#createInstance(Path root)

Here is a working test code:

    @Test
    void test_twins_mode() {
        Path root = outputDir.resolve("Materials")
        Store store = new StoreImpl(root)

The following fragment looks bad.

... = new StoreImpl(root)

It is a bad idea to ask users to be aware of the StoreImpl class.

The following code (Java 8's static method in the interfaces) is desired:

... = Store.newInstance(root)

Entries in a MetadataPattern is not sorted at all

スクリーンショット 2021-08-14 13 53 23

In the Bootstrap Menu part, a Metadata is displayed

{ "URL.protocol": "https", "URL.fragment": "appointment", "URL.path": "/",  }

here the keys ARE NOT sorted.

But in the detail information section, the same Metadata is dispalayed

{ "URL.fragment": "appointment", "URL.host": "katalon-demo-cura.herokuapp.com", "URL.path": "/", "URL.protocol": "https", "selector": "#appointment" }

here the keys are sorted.

Inconsistent.

Store should implement findAllJobTimestampsPriorTo() methods

com.kazurayam.materialstore.store.Store should implement the following methods

    JobTimestamp findLatestJobTimestamp(JobName jobName)
    
    JobTimestamp findJobTimestampPriorTo(JobName jobName, JobTimestamp jobTimestamp)
    
    List<JobTimestamp> findAllJobTimestampsPriorTo(JobName jobName, JobTimestamp jobTimestamp)

in build.gradle, add "pinpoint" task

When I ran the gradle test command, often I got 2 or more tests failed. So I want to rerun those failing tests only in order to shorten the time to rerun them.

I wrote in the build.gradle the following task definition.

task pinpoint(type: Test) {
    include "**/store/differ/ImageDifferToPNGTest.class"
    include "**/store/DifferDriverImplTest.class"
    include "**/store/StoreImplTest.class"
    outputs.upToDateWhen {false}
}

but it does not ran.
I do not know why?

DiffArtifacts class is demanded

In v0.1.0 there is a working code:

        // make diff
        List<DiffArtifact> stuffedDiffArtifacts =
                store.makeDiff(expected, actual, ["URL.file"] as Set)

        // compile HTML report
        DiffReporter reporter = store.newReporter(jobName)
        reporter.reportDiffs(stuffedDiffArtifacts, "index.html")

This should rather be:

        // make diff
        DiffArtifacts stuffedDiffArtifacts =
                store.makeDiff(expected, actual, ["URL.file"] as Set)

        // compile HTML report
        DiffReporter reporter = store.newReporter(jobName)
        reporter.reportDiffs(stuffedDiffArtifacts, "index.html")

DiffArtifacts would wrap a List, and provides a method that decides FAILED/SUCCESS.

And the DiffArtifacts can implement a method that can tell if one or more entry DiffArtifacts has the diffRation > the criteria.

I want Material#toFile() method, Material#toPath() method

we need Material#toFile() method.

Why?

Store#select() returns an instance of List<Material>. Then what to do next?

The following code should be possible:

List<Material> materials = store.select(jobName, jobTimestamp, new Metadata([:], FileType.HTML))
materials.each { Material mate ->
    println "-" * 80
    print mate.toFile().text()
}

Modal dialog of the 3rd and following accordion items do not show up.

In the "VisualTestingInKatalonStudio_revive" project, the "VisualTestingTwins" script produced a HTML report where I saw many accordion items.

As for the 1st and 2nd items, the "Show in Modal" button worked.

But as for the 3rd and following items, the "Show in Modal" button does not work. The browser screen got darkened and does not do anything. See the following screenshot.

スクリーンショット 2021-07-24 21 33 40

Helper method `store.compareToReport()`

I wrote this code:

// pickup the materials that belongs to the 2 "profiles"
List<Material> left = store.select(jobName, jobTimestamp,
			new MetadataPattern.Builder([ "profile": profile1 ]).build()
			)

List<Material> right = store.select(jobName, jobTimestamp,
			new MetadataPattern.Builder([ "profile": profile2 ]).build()
			)

// difference greater than the criteria should be warned
double criteria = 0.0d

// make DiffArtifacts
DiffArtifacts stuffedDiffArtifacts =
	store.makeDiff(left, right, 
		new MetadataIgnoredKeys.Builder()
			.ignoreKey("profile")
			.ignoreKey("URL.host")
			.build())
int warnings = stuffedDiffArtifacts.countWarnings(criteria)

// compile HTML report
Path reportFile = store.reportDiffs(jobName, stuffedDiffArtifacts, criteria, jobName.toString() + "-index.html")

This worked, but not very good. This requires programmers to be aware of the internal of materialstore API. Those internals should be hidden to them.

It would be nice if I can write in a more compact syntax as follows, which does the same work as above:

CompletedReport report = store.compareToReport({
    jobName = jobName
    jobTimestamp= jobTimestamp
    metadataPatternLeft = new MetadataPattern.Builder([ "profile": profile1 ]).build()
    metadataPatternRight = new MetadataPattern.Builder([ "profile": profile2 ]).build()
    criteria = 0.0d
    metadataIgnoredKeys = new MetadataIgnoredKeys.Builder()
			.ignoreKey("profile")
			.ignoreKey("URL.host")
			.build()
    reportName: jobName.toString() + "-index.html"
})

This is equivalent to

... store.compareToReport({
    delegate.jobName = jobName
    delegate.jobTimestamp = jobTimestamp
    delegate.metadataPatternLeft ...
})

MaterialstoreException was raised; 2 MObjects with different metadata but with the same ID value

Caused by: com.kazurayam.materialstore.MaterialstoreException: fileType={"FileType":{"extension":"png","mimeTypes":["image/png"]}} metadata={"URL":"https://katalon-demo-cura.herokuapp.com/profile.php#login","URL.file":"/profile.php","URL.host":"katalon-demo-cura.herokuapp.com","profile":"CURA_DevelopmentEnv"}: MObject is already in the Store. Metadata is duplicating. Give more detailed metadata to make this object uniquely identifiable.
	at com.kazurayam.materialstore.store.Jobber.write(Jobber.groovy:146)

where MObject with metadata={"URL":"https://katalon-demo-cura.herokuapp.com/profile.php#login","URL.file":"/profile.php","URL.host":"katalon-demo-cura.herokuapp.com","profile":"CURA_DevelopmentEnv"} was already there, and the ID value of the 2 MObjects were equal ac9be9a1053828f1e12e1cee4d66ff66adf60f9f

Metadata#toString() method is questionable

In Metadata#toString(), I wrote it as this:

    @Override
    String toString() {
        //return new JsonOutput().toJson(metadata_)
        StringBuilder sb = new StringBuilder()
        int entryCount = 0
        sb.append("{")
        assert metadata_ != null, "metadata_ is null before iterating over keys"
        //println "keys: ${metadata_.keySet()}"
        List<String> keys = new ArrayList<String>(metadata_.keySet())
        Collections.sort(keys)
        keys.each { key ->
            println "key is ${key}"
            if (entryCount > 0) {
                sb.append(", ")    // comma followed by a white space
            }
            sb.append('"')
            assert copy != null, "metadata_ is null for key=${key}"
            sb.append(JsonUtil.escapeAsJsonString(key))
            sb.append('"')
            sb.append(':')
            sb.append('"')
            sb.append(JsonUtil.escapeAsJsonString(metadata_.get(key)))
            sb.append('"')
            entryCount += 1
        }
        sb.append("}")
        return sb.toString()
    }

NullPointerException was raised at metadata_.get(key) saying metadata_ is null.

I changed it as follows:

    @Override
    String toString() {
        //return new JsonOutput().toJson(metadata_)
        StringBuilder sb = new StringBuilder()
        int entryCount = 0
        sb.append("{")
        assert metadata_ != null, "metadata_ is null before iterating over keys"
        //println "keys: ${metadata_.keySet()}"
        List<String> keys = new ArrayList<String>(metadata_.keySet())
        Map<String,String> copy = new HashMap<String,String>(metadata_)
        Collections.sort(keys)
        keys.each { key ->
            println "key is ${key}"
            if (entryCount > 0) {
                sb.append(", ")    // comma followed by a white space
            }
            sb.append('"')
            assert copy != null, "metadata_ is null for key=${key}"
            sb.append(JsonUtil.escapeAsJsonString(key))
            sb.append('"')
            sb.append(':')
            sb.append('"')
            sb.append(JsonUtil.escapeAsJsonString(copy.get(key)))
            sb.append('"')
            entryCount += 1
        }
        sb.append("}")
        return sb.toString()
    }

I made the copy of the metadata_ variable. This worked.

But I am not comfortable with this fix. I still do not understand why NEP was raised.

Store#select() method should be changed

Store interface has 2 methods:

    List<Material> select(JobName jobName, JobTimestamp jobTimestamp,
                          MetadataPattern metadataPattern)

    List<Material> select(JobName jobName, JobTimestamp jobTimestamp,
                          MetadataPattern metadataPattern, FileType fileType)

I think these 2 are not good. I only need one. And the FileType should be the 3rd argument, like Store#write() method.

MetadataPattern should use Reglar Expression, rather than glob `*`

v0.1.0 MetadataPattern works on the glob * for matching.

    @Test
    void test_match_asterisk_pattern_matches_everything() {
        Metadata target = new MetadataImpl.Builder(["key":"value"]).build()
        MetadataPattern pattern = new MetadataPattern.Builder(["key":"*"]).build()
        assertTrue(target.match(pattern))
    }

It should use Regular Expression, rather than glob * for more expressiveness:

        MetadataPattern pattern = new MetadataPattern.Builder(["key":".*Env"]).build()

commons-io is not needed

build.gradle has

dependencies {

    // https://mvnrepository.com/artifact/commons-io/commons-io
    implementation group: 'commons-io', name: 'commons-io', version: "${commonsioVersion}"

but possibly we can remove this dependency.

MetadataIgnoredKey class looks complicated

I have the following sample code.

        // make diff
        DiffArtifacts stuffedDiffArtifacts =
                store.makeDiff(left, right,
                        new MetadataIgnoredKeys.Builder()
                                .ignoreKey("profile")
                                .ignoreKey("URL")
                                .ignoreKey("URL.host")
                                .build())

This looks complicated.

I would rather like:

        // make diff
        DiffArtifacts stuffedDiffArtifacts =
                store.makeDiff(left, right, 
                        MetadataIgnoredKeys.of("profile", "URL", "URL.host"))

SLF4J: Class path contains multiple SLF4J bindings.

When I run StoreImplTest, I see the following messages

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/kazuakiurayama/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.2.3/7c4f3c474fb2c041d8028740440937705ebb473a/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/kazuakiurayama/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-simple/1.7.31/cf5df9007f6fb85f04728eec1dd96de73533fec7/slf4j-simple-1.7.31.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]

Disturbing.

StoreImpl#zipMaterials() should warn if it finds 2 or more zipping candidates

StoreImpl class has the method:
List<DiffArtifact> zipMaterials(List<Material> expectedList, List<Material> actualList, Set<String> metadataKeys)

A "expected" Material may find 2 more more matching "actual" Materials. Or an "actual" Material may find 2 ore more matching "expected" material when the given metadataKeys is not specific enough.

The current implementation in v0.1.0 is not robust enough. It just does not care of the duplications; emits no warning, no Exception.

MetadataPattern class is too complicated

In the com.kazurayam.materialstoreexamples.Ex03TakingScreenshotsTest, I wrote the following code in Java:

@AfterAll
    public static void afterAll() throws IOException, InterruptedException {
        // list all of 4 cities
        List<Material> materialList = store.select(jobName, jobTimestamp, MetadataPattern.ANY);
        store.reportMaterials(jobName, materialList, "fullList.html");

        // list 2 cities of which name stats with "To"
        Map<String, Object> mp = new ImmutableMap.Builder()
                .put("city", Pattern.compile("To.*"))
                .build();
        List<Material> selected = store.select(jobName, jobTimestamp,
                new MetadataPattern.Builder((MapLike) mp).build());
        store.reportMaterials(jobName, materialList, "selected.html");
...

This is ugly. ImmutableMap? new MetadataPattern.Builder((MapLike)mp).build()? What's going on?

Why do we have to write so complicated code?

This should be simpler and intuitive. Otherwise, nobody would like it.

simpler package name is desired

v 0.1.0 has the following package

import com.kazurayam.materialstore.store.Store
import com.kazurayam.materialstore.store.StoreImpl
import com.kazurayam.materialstore.store.JobName
import com.kazurayam.materialstore.store.JobTimestamp

This looks redundant. These should rather be:

import com.kazurayam.materialstore.Store
import com.kazurayam.materialstore.StoreImpl
import com.kazurayam.materialstore.JobName
import com.kazurayam.materialstore.JobTimestamp

Metadata object should be immutable

In v0.1.0, com.kazurayam.materialstore.store.Metadata class implements #put() and #putAll() method.
That's a bad idea.
The Metadata object should be immutable.
I should remove the #put method

StoreTest emitted a log of DEBUG level messages via SLF4J + Logback

16:37:06.573 [Test worker] DEBUG io.github.bonigarcia.wdm.cache.ResolutionCache - Removing resolution chrome=92 from cache (expired on Wed Jul 28 23:16:39 JST 2021)
16:37:06.625 [Test worker] DEBUG org.apache.hc.client5.http.impl.classic.InternalHttpClient - ex-00000001: preparing request execution
16:37:06.639 [Test worker] DEBUG org.apache.hc.client5.http.protocol.RequestAddCookies - Cookie spec selected: strict
16:37:06.656 [Test worker] DEBUG org.apache.hc.client5.http.protocol.RequestAuthCache - Auth cache not set in the context
16:37:06.656 [Test worker] DEBUG org.apache.hc.client5.http.impl.classic.ProtocolExec - ex-00000001: target auth state: UNCHALLENGED
16:37:06.660 [Test worker] DEBUG org.apache.hc.client5.http.impl.classic.ProtocolExec - ex-00000001: proxy auth state: UNCHALLENGED
16:37:06.661 [Test worker] DEBUG org.apache.hc.client5.http.impl.classic.ConnectExec - ex-00000001: acquiring connection with route {s}->https://raw.githubusercontent.com:443
16:37:06.661 [Test worker] DEBUG org.apache.hc.client5.http.impl.classic.InternalHttpClient - ex-00000001: acquiring endpoint (3 MINUTES)
16:37:06.667 [Test worker] DEBUG org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager - ex-00000001: endpoint lease request (3 MINUTES) [route: {s}->https://raw.githubusercontent.com:443][total available: 0; route allocated: 0 of 5; total allocated: 0 of 25]
16:37:06.681 [Test worker] DEBUG org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager - ex-00000001: endpoint leased [route: {s}->https://raw.githubusercontent.com:443][total available: 0; route allocated: 1 of 5; total allocated: 1 of 25]
16:37:06.719 [Test worker] DEBUG org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager - ex-00000001: acquired ep-00000000
16:37:06.719 [Test worker] DEBUG org.apache.hc.client5.http.impl.classic.InternalHttpClient - ex-00000001: acquired endpoint ep-00000000
16:37:06.719 [Test worker] DEBUG org.apache.hc.client5.http.impl.classic.ConnectExec - ex-00000001: opening connection {s}->https://raw.githubusercontent.com:443
16:37:06.722 [Test worker] DEBUG org.apache.hc.client5.http.impl.classic.InternalHttpClient - ep-00000000: connecting endpoint (30 SECONDS)
16:37:06.722 [Test worker] DEBUG org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager - ep-00000000: connecting endpoint to https://raw.githubusercontent.com:443 (30 SECONDS)
16:37:06.776 [Test worker] DEBUG org.apache.hc.client5.http.impl.io.DefaultHttpClientConnectionOperator - http-outgoing-0: connecting to raw.githubusercontent.com/185.199.111.133:443
16:37:06.776 [Test worker] DEBUG org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory - Connecting socket to raw.githubusercontent.com/185.199.111.133:443 with timeout 30 SECONDS
16:37:06.910 [Test worker] DEBUG org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory - Enabled protocols: [TLSv1.2]
16:37:06.911 [Test worker] DEBUG org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory - Enabled cipher suites: [TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
16:37:06.911 [Test worker] DEBUG org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory - Starting handshake
16:37:07.198 [Test worker] DEBUG org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory - Secure session established
16:37:07.198 [Test worker] DEBUG org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory -  negotiated protocol: TLSv1.2
16:37:07.198 [Test worker] DEBUG org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory -  negotiated cipher suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
16:37:07.198 [Test worker] DEBUG org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory -  peer principal: CN=www.github.com, O="GitHub, Inc.", L=San Francisco, ST=California, C=US
16:37:07.202 [Test worker] DEBUG org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory -  peer alternative names: [www.github.com, *.github.com, github.com, *.github.io, github.io, *.githubusercontent.com, githubusercontent.com]
16:37:07.203 [Test worker] DEBUG org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory -  issuer principal: CN=DigiCert SHA2 High Assurance Server CA, OU=www.digicert.com, O=DigiCert Inc, C=US
16:37:07.204 [Test worker] DEBUG org.apache.hc.client5.http.impl.io.DefaultHttpClientConnectionOperator - http-outgoing-0: connection established 192.168.0.9:57681<->185.199.111.133:443
16:37:07.208 [Test worker] DEBUG org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager - ep-00000000: connected http-outgoing-0
16:37:07.208 [Test worker] DEBUG org.apache.hc.client5.http.impl.classic.InternalHttpClient - ep-00000000: endpoint connected
16:37:07.210 [Test worker] DEBUG org.apache.hc.client5.http.impl.classic.MainClientExec - ex-00000001: executing GET /bonigarcia/webdrivermanager/master/src/main/resources/commands.properties HTTP/1.1
16:37:07.211 [Test worker] DEBUG org.apache.hc.client5.http.impl.classic.InternalHttpClient - ep-00000000: start execution ex-00000001
16:37:07.212 [Test worker] DEBUG org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager - ep-00000000: executing exchange ex-00000001 over http-outgoing-0
16:37:07.213 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 >> GET /bonigarcia/webdrivermanager/master/src/main/resources/commands.properties HTTP/1.1
16:37:07.214 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 >> user-agent: Apache-HttpClient/5.0
16:37:07.214 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 >> accept-encoding: gzip, deflate, br
16:37:07.214 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 >> cache-control: max-age=0
16:37:07.214 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 >> connection: keep-alive
16:37:07.214 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 >> Host: raw.githubusercontent.com
16:37:07.214 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> "GET /bonigarcia/webdrivermanager/master/src/main/resources/commands.properties HTTP/1.1[\r][\n]"
16:37:07.215 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> "user-agent: Apache-HttpClient/5.0[\r][\n]"
16:37:07.215 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> "accept-encoding: gzip, deflate, br[\r][\n]"
16:37:07.215 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> "cache-control: max-age=0[\r][\n]"
16:37:07.215 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> "connection: keep-alive[\r][\n]"
16:37:07.216 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> "Host: raw.githubusercontent.com[\r][\n]"
16:37:07.217 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 >> "[\r][\n]"
16:37:07.609 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "HTTP/1.1 200 OK[\r][\n]"
16:37:07.609 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "Connection: keep-alive[\r][\n]"
16:37:07.609 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "Content-Length: 728[\r][\n]"
16:37:07.610 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "Cache-Control: max-age=300[\r][\n]"
16:37:07.610 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox[\r][\n]"
16:37:07.610 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "Content-Type: text/plain; charset=utf-8[\r][\n]"
16:37:07.610 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "ETag: W/"f670816d6bfbef80584dadbb549bb9c4ccd5de869d5b63cc2a694a74613014b5"[\r][\n]"
16:37:07.611 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "Strict-Transport-Security: max-age=31536000[\r][\n]"
16:37:07.611 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "X-Content-Type-Options: nosniff[\r][\n]"
16:37:07.611 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "X-Frame-Options: deny[\r][\n]"
16:37:07.611 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "X-XSS-Protection: 1; mode=block[\r][\n]"
16:37:07.612 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "X-GitHub-Request-Id: 2F7E:0286:5B3C:6617:6101BBF3[\r][\n]"
16:37:07.612 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "Content-Encoding: gzip[\r][\n]"
16:37:07.612 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "Accept-Ranges: bytes[\r][\n]"
16:37:07.613 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "Date: Thu, 29 Jul 2021 07:37:07 GMT[\r][\n]"
16:37:07.613 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "Via: 1.1 varnish[\r][\n]"
16:37:07.613 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "X-Served-By: cache-tyo11937-TYO[\r][\n]"
16:37:07.613 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "X-Cache: HIT[\r][\n]"
16:37:07.614 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "X-Cache-Hits: 1[\r][\n]"
16:37:07.614 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "X-Timer: S1627544228.561047,VS0,VE215[\r][\n]"
16:37:07.614 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "Vary: Authorization,Accept-Encoding[\r][\n]"
16:37:07.614 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "Access-Control-Allow-Origin: *[\r][\n]"
16:37:07.615 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "X-Fastly-Request-ID: 27fa69f67316545cc834d86fef6cb3675e546107[\r][\n]"
16:37:07.615 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "Expires: Thu, 29 Jul 2021 07:42:07 GMT[\r][\n]"
16:37:07.615 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "Source-Age: 0[\r][\n]"
16:37:07.616 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "[\r][\n]"
16:37:07.637 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << HTTP/1.1 200 OK
16:37:07.638 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << Connection: keep-alive
16:37:07.639 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << Content-Length: 728
16:37:07.640 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << Cache-Control: max-age=300
16:37:07.641 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox
16:37:07.641 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << Content-Type: text/plain; charset=utf-8
16:37:07.642 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << ETag: W/"f670816d6bfbef80584dadbb549bb9c4ccd5de869d5b63cc2a694a74613014b5"
16:37:07.642 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << Strict-Transport-Security: max-age=31536000
16:37:07.642 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << X-Content-Type-Options: nosniff
16:37:07.643 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << X-Frame-Options: deny
16:37:07.644 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << X-XSS-Protection: 1; mode=block
16:37:07.646 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << X-GitHub-Request-Id: 2F7E:0286:5B3C:6617:6101BBF3
16:37:07.647 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << Content-Encoding: gzip
16:37:07.649 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << Accept-Ranges: bytes
16:37:07.650 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << Date: Thu, 29 Jul 2021 07:37:07 GMT
16:37:07.651 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << Via: 1.1 varnish
16:37:07.652 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << X-Served-By: cache-tyo11937-TYO
16:37:07.653 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << X-Cache: HIT
16:37:07.653 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << X-Cache-Hits: 1
16:37:07.654 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << X-Timer: S1627544228.561047,VS0,VE215
16:37:07.654 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << Vary: Authorization,Accept-Encoding
16:37:07.655 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << Access-Control-Allow-Origin: *
16:37:07.655 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << X-Fastly-Request-ID: 27fa69f67316545cc834d86fef6cb3675e546107
16:37:07.656 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << Expires: Thu, 29 Jul 2021 07:42:07 GMT
16:37:07.656 [Test worker] DEBUG org.apache.hc.client5.http.headers - http-outgoing-0 << Source-Age: 0
16:37:07.669 [Test worker] DEBUG org.apache.hc.client5.http.impl.classic.MainClientExec - ex-00000001: connection can be kept alive for 3 MINUTES
16:37:07.700 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "[0x1f][0xffffff8b][0x8][0x0][0x0][0x0][0x0][0x0][0x0][0x3][0xffffffb5]V]o[0xffffffda]0[0x14]}[0xffffffe7]W\[0xffffff81]*[0xffffffb5][0x12][0x1f][[M[0xffffffd3]$[0x1e]hF[0xffffffbb]i [0x18][0xffffffb4][0xffffffdd]&[0xfffffff9][0xffffffc5]Mn[0xffffff82][0xffffffa5]$Fv([0xffffffed]~[0xfffffffd]l[0xffffffe7]c$u[0x6]k[0xffffffe8][l_[0xffffff9f]s|[0xffffffef]q|;[0xffffffe0][0xfffffff0]([0xffffffa2][0xffffffb1]'[0xffffffc1][0xffffffa3][0x9]}[0xffffffa0][0x12][[0x1d][0xffffffb8]]1[0x9]>[0xb][0x11]\[0x1e]'[0xffffff94][0xffffffc5][0x12][0xffffff92][0xffffff95][0x1e]d[0xffffffa1][0x9][0xffffff87][0x7][0x4]|Bw[0xffffff93][0xffffffa0][0x7],6[0xffffffcb]r[0xffffff85]a[0xffffffa8][0x7]\x(t[0xffffff90][0xffffff87][0x9][0xffffffba][0xffffff89]Y|D![0x19]Wk>[0xffffffa0][0xfffffffa][0xe][0x4]b[0xc][0xf][0xffffff82]o[0xffffffa5]ZP[0xffffff94][0xffffffa7][0xffffffce]J[0xfffffff0][0x8][0xffffffbb]p[0xffffffcd][0x4][0xfffffffa][0xfffffffc][0xffffffa9][0xb][0xffffffb3]5[\n]"
16:37:07.702 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "[0xffffffda][0xffffff85][0xffffffb1][0x17][0xffffffa8]Y[0xffffffb3][0xffffffca]6[0xffffffd1][0xffffff99][0xffffffc6][0xfffffff7][0xffffff98][0xffffffef][0xffffffa3][0xffffffc0]8[0x1][0xffffffae]c[0x12][0x16][0x7] [0xffffff9f]e[0xffffff82][0xffffff91][0xffffff84][0xffffffd3][0x1f],[0xfffffff6][0x14]j[0x17]&,[0xffffffde]([0xffffff98])ua[0xffffffb6]<k)[0xffffff8e][0xffffff94][0xffffffa2][0xffffff95][0x1d][0xffffffa3][0xffffffef][0xffffff9a]a[0x7f][0xffffff9b]n[0xffffffe8][0xffffffbf]{?t#[0xffffffaf][0xffffffaf]N[0x5][0x3][0x7][0xffffffb6][0x11]sMJL[0x1a][0xffffffb6]+[0xffffffc5][0x7]1[0xffffff8d]p[0xffffffd8]>[0xffffff99]/f7[0xffffff8b][0xffffffd1][0xfffffff4][0xfffffffa][0xffffffeb]d[0xffffffbc]<[0xfffffffd][0xfffffff9][0xfffffff1][0xffffffc3][0xffffffd9]'2$[0xffffffe4][0xffffff84][0xffffff90][0x1b][0xffffffce][0xffffff83][0x10][0x9]Iy[0x8][0x19][0xffffffad][0xffffffd7]!s[0xffffff95]>[0x1e][0x13][0xffffff92][0xffffffb1])[0xfffffff8]6[0x4][0xffffff98][0xffffffc0]}[0xffffff96][0xffffff90][0xffffffc1]#[\r]7[0xfffffff5][0xffffffa2][0xffffffce][0xf][0x14]5[0xffffff99]9[0xffffffa3][0xffffffc9]h>[0xffffffff]<[0xffffffba][0x1d][0xffffffbd][0xffffffa5][0xffffff9e][0xffffff8b]W$[0xffffffe9]-[0xfffffff5]\[0xe][0x17][0xffffffe3][0x1b][0xfffffff8]~7^[0xfffffffc][0xffffff82]/[0xffffffdf][0xffffff9c];[0xffffffb2][0xffffffe4]~[0xffffffb2][0xffffffa5][0x2]s[0xffffffb6][0xffffff8c][0xffffffec]jr[0xffffff85][0xffffffd4]5x[0xffffffb9][0x17][U[0xffffffd0]P;F[0xfffffffb] 0[{[0xffffffe9]4[0xfffffff4]zu[0x1b]"[0xffffffea][0xffffffea][0xfffffff0][0xffffffc1][0xffffffce]A[0xffffffe4] [0xffffffe5][0xffffffcd][0xffffffcc][0xffffffd6][0xffffffa7][0xffffffeb][0xfffffff5][0xffffffc0]Q[0xffffff97]H[0xffffffb9]U[0xe][0xffffff94][0x17]g[0xffffffcb]r[0xffffffc0].[0xffffffba][0xffffffb2]h[0xffffffe6][0xfffffffe][0xffffff82][0xffffffc8]O[0xffffffc7][0xffffffcd]L[0xffffff9a][0xffffffe7][0x7f][0xffffffca][0x7f][0xffffffb3]0[0xffffffa4]9[0x9]!9[0xfffffffa][0xffffffbe][0xffffffa4][0xffffffbf]Pq[0xffffffa8]+k[0xffffffaf][0xffffffca][0xfffffff1][0xffffffa4]\[0xffffffec][0x18][0xffffffa0]]v@FR%kk[0xf]8[0x1b][0xffffffa1][0xffffffff] [0xfffffff7][0xffffffd5][0xffffffca][0xffffffe6][0xfffffff8][0xffffff85][0x17][0xffffffb2][0x9][0xffffffe8]=[0xffffffbe][0xc][0xffffffb2][0xffffffd6]?c[0xffffffb1]U~[0x17]K[0x15][[0xffffffff][0xffffffda][\n]"
16:37:07.703 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "LT[0xffffff83]#[0xfffffffe][0xffffff8b][0xffffffa6][0xffffffcc][0x15]\[0xffffffaa]L[0x10][0xffffffa2]i*[0xffffff97]-[0xffffff92][0xffffff86]n_[0xffffffb2][0xffffffcb][0xffffff9a]^S[0xfffffff4]7[0xffffff94]sQ{[0xfffffff3][0xffffffff][0xffffff92][0x19][0xffffffae][0x7f]^}[0xffffff83]Y[0x14];[0xffffffca]w[0xfffffff6][0xfffffff4][0xffffffbc][0xffffffed][0xffffffe6][0xffffff9b]xk[0xffffffdd][0xb]VSW[[0xfffffff9][0xffffffcb][0x11]P[0xffffffba][0xfffffff8][0xffffffe6][0xffffffb5]+X[0xffffffcc][0xffffffbb][0xfffffff6][0xffffffff]n[0xffffffb0]=[0x2]s[0xffffffc1][0x3]A#I[0xffffff88][0xffffffa1] $[0xffffffa4][0xffffff9b][0xffffffd8]U[0xffffffbb][0xfffffff6][0xffffffa6][0xffffffbb]"[0xffffffa2]I[0xfffffff9][0xffffff8f]&[0xffffffe2][0xffffffd0][0xffffffe7][0xffffffc7][0xffffff96][0xffffff89][0xffffff86][0xffffffd4][0xffffff97][\r][0xffffffce][0x7f](u[0xffffff85][0xffffffbb][0xfffffff0][0xffffffa5][0x19][0xffffffda][0xffffffec][0xffffff98][0xffffffc6]Y[0xfffffffd]h8m6[0xffffff9c]U[0xffffffc1][0xfffffff2][0xffffffd6]H[0xfffffff5]W[0xffffffe5][0x17]NM[0x1c][0xffffffc7][0xffffff84]9|[0xffffff83]7[0xffffffbf][0xffffffa4][0xffffffa5][0xfffffff1][0xfffffffb]slAM[0xffffffda][0xffffffa2]ck[0xffffffa9]o[0xffffff89][\n]"
16:37:07.703 [Test worker] DEBUG org.apache.hc.client5.http.wire - http-outgoing-0 << "[0xffffffa6][0xfffffffd][0xffffffdd][0xffffff90]F,[0xffffffec][0xffffff97][0xffffffcf][0xfffffff4][0xffffffb2][0xffffff86]}[0xffffffc7]<u[0xffffff9b][0xffffffce][0xffffff8b]M[0xffffffb5][0xfffffffd][0xffffff93][0xffffff8e][0xffffffb6]:7[0xffffff97]i3[0xffffffaf]cA[0xffffffed][0xffffffc0][0xffffff92][0xfffffffa]T[0xffffffb0][0x2]^[0xffffff9a][0xffffffa1][0x1d]<[\r][0xffffffb5]A[0xffffffa7]+;[0xffffffc0][0x7f][0x0][0xffffffb2]$|[0xffffff88][0x1d][\r][0x0][0x0]"
16:37:07.705 [Test worker] DEBUG org.apache.hc.client5.http.impl.classic.InternalHttpClient - ep-00000000: releasing valid endpoint
16:37:07.705 [Test worker] DEBUG org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager - ep-00000000: releasing endpoint
16:37:07.706 [Test worker] DEBUG org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager - ep-00000000: connection http-outgoing-0 can be kept alive for 3 MINUTES
16:37:07.707 [Test worker] DEBUG org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager - ep-00000000: connection released [route: {s}->https://raw.githubusercontent.com:443][total available: 1; route allocated: 1 of 5; total allocated: 1 of 25]
16:37:07.708 [Test worker] DEBUG io.github.bonigarcia.wdm.versions.VersionDetector - Detecting chrome version using online commands.properties
16:37:07.713 [Test worker] DEBUG io.github.bonigarcia.wdm.versions.Shell - Running command on the shell: [/Applications/Google Chrome.app/Contents/MacOS/Google Chrome, --version]
16:37:07.814 [Test worker] DEBUG io.github.bonigarcia.wdm.versions.Shell - Result: Google Chrome 92.0.4515.107
16:37:07.823 [Test worker] DEBUG io.github.bonigarcia.wdm.cache.ResolutionCache - Resolution chrome92=92.0.4515.43 in cache (valid until 18:47:49 29/07/2021 JST)
16:37:07.823 [Test worker] INFO io.github.bonigarcia.wdm.WebDriverManager - Using chromedriver 92.0.4515.43 (resolved driver for Chrome 92)
16:37:07.826 [Test worker] DEBUG io.github.bonigarcia.wdm.cache.ResolutionCache - Storing resolution chrome=92 in cache (valid until 17:37:07 29/07/2021 JST)
16:37:07.865 [Test worker] DEBUG io.github.bonigarcia.wdm.WebDriverManager - Driver chromedriver 92.0.4515.43 found in cache
16:37:07.870 [Test worker] INFO io.github.bonigarcia.wdm.WebDriverManager - Exporting webdriver.chrome.driver as /Users/kazuakiurayama/.cache/selenium/chromedriver/mac64/92.0.4515.43/chromedriver

com.kazurayam.materialstore.store.DifferDriverImpl - expected Material was NULL_OBJECT.

When I ran com/kazurayam/materialstore/store/StoreTest.groovy, I saw the following WARN message

[Test worker] WARN com.kazurayam.materialstore.store.DifferDriverImpl - expected Material was NULL_OBJECT. actual={"jobName":"test_twins_mode","jobTimestamp":"20210724_192404","ID":"5c0d1fd25c400be024642c7a3f3297b00c760f00","fileType":"html","metadata":"{\"URL\":\"http://demoaut-mimic.kazurayam.com/\",\"URL.file\":\"/\",\"URL.host\":\"demoaut-mimic.kazurayam.com\",\"category\":\"page source\",\"profile\":\"DevelopmentEnv\"}"}
[Test worker] WARN com.kazurayam.materialstore.store.DifferDriverImpl - expected Material was NULL_OBJECT. actual={"jobName":"test_twins_mode","jobTimestamp":"20210724_192404","ID":"272ff864911bf64b3a40362993c0b7a5ef55d116","fileType":"png","metadata":"{\"URL\":\"http://demoaut-mimic.kazurayam.com/\",\"URL.file\":\"/\",\"URL.host\":\"demoaut-mimic.kazurayam.com\",\"category\":\"page source\",\"profile\":\"DevelopmentEnv\"}"}

a shorthand method Store#reportDiffs(JobName, List<DiffArtifact>, String) is required

There is a working code like:

        // make diff
        List<DiffArtifact> stuffedDiffArtifacts =
                store.makeDiff(expected, actual, ["URL.file"] as Set)

        // compile HTML report
        DiffReporter reporter = store.newReporter(jobName)
        reporter.reportDiffs(stuffedDiffArtifacts, "index.html")

This should rather be:

        List<DiffArtifact> stuffedDiffArtifacts =
                store.makeDiff(expected, actual, ["URL.file"] as Set)

        Path report = store.reportDiffs(jobName, stuffedDiffArtifacts, "index.html")

MetadataPattern should use Regular Expression rather than the Glob `*`

v0.1.0 MetadataPattern works on the glob * for matching.

    @Test
    void test_match_asterisk_pattern_matches_everything() {
        Metadata target = new MetadataImpl.Builder(["key":"value"]).build()
        MetadataPattern pattern = new MetadataPattern.Builder(["key":"*"]).build()
        assertTrue(target.match(pattern))
    }

It should use Regular Expression, rather than glob * for more expressiveness:

        MetadataPattern pattern = new MetadataPattern.Builder(["key":".*Env"]).build()

SLF4J related message

When I executed com/kazurayam/materialstore/demo/OpenWeatherMapClinet, I saw the following messages in the console. I want to get rid of them.

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

Need to re-design the way how to find pairs of Materials to diff

at 4bf8b34 i created WebAPITestingChronos. This code demostrates how I want the code should be.

    void execute() {
        initDir(root_)
        Store store = Stores.newInstance(root_)
        JobName jobName = new JobName("WebAPITestingChronos")
        JobTimestamp currentTimestamp = JobTimestamp.now()

        // make Web API request to OpenWeatherMap
        // to get a weather forecast data in JSON,
        // save the data into the Materials directory
        doOpenWeatherAction(store, jobName, currentTimestamp, ["id": "498817"])
        doOpenWeatherAction(store, jobName, currentTimestamp, ["q": "Hachinohe"])

        // retrieve a previous weather data of the target city
        JobTimestamp baseTimestamp = currentTimestamp.minusSeconds(1)
        JobTimestamp previousTimestamp = store.findJobTimestampPriorTo(jobName, baseTimestamp)
        if (previousTimestamp == JobTimestamp.NULL_OBJECT) {
            throw new MaterialstoreException(
                    "JobTimestamp prior to ${baseTimestamp} is not found. We will quit.")
        }
        List<Material> previousData = store.select(jobName, previousTimestamp)

        // retrieve the current weather data of the target city
        List<Material> currentData  = store.select(jobName, currentTimestamp)

        // make diff
        DiffArtifacts stuffedDiffArtifacts =
                store.makeDiff(previousData, currentData)

        int countWarnings = stuffedDiffArtifacts.countWarnings(0.0d)
        println "countWarnings: ${countWarnings}"

        // compile HTML report
        Path file = store.reportDiffs(jobName, stuffedDiffArtifacts, "index.html")
        println "output: ${file.toString()}"
    }
    private static void doOpenWeatherAction(Store store,
                                     JobName jobName,
                                     JobTimestamp jobTimestamp,
                                     Map<String, String> param) {
        // make query for data to OpenWeatherMap
        OpenWeatherMapClient client = new OpenWeatherMapClient()
        String weatherData = client.getOpenWeatherData(param)

        // save the data into the store
        Metadata metadata = new Metadata(param)
        store.write(jobName, jobTimestamp, FileType.JSON,
                metadata,
                JsonOutput.prettyPrint(weatherData))
    }
  1. When I make a Metadata object for a Material object to store, I want it to be as simple as possible. For example, {"id":"498817"} or {"q":"Hachinohe"}.
  2. When I select a list of Materials, I want to write: store.select(jobName, currentTimestamp). In other words, I want to omit specifying a Metadata. It is because I am aware that want to pick up all the Materials in the job directory. No need to choose.
  3. When I make DiffArtifacts, i want to wriate: DiffArtifacts stuffedDiffArtifacts = store.makeDiff(previousData, currentData). In other words, I want to omit specifying a MetadataPattern. It is because I am aware that I want to take diff of "all files in the job directory".
  4. The report compiled looks odd.

スクリーンショット 2021-07-28 11 39 36

- In the report, in each accordion title, `{}` is displayed. `{}` is not useful at all to identify lines. - a DiffArtifact has a Material of `{"id":"498817"}` and a Material of `{"q":"Hachinohe"}`. These 2 Materials should not be zipped as a pair.

Builder pattern for "Metadata" class

An example of constructing a Metadata object is like this:

            new Metadata([
                        "category": "screenshot",
                        "profile": profile,
                        "URL": url.toExternalForm(),
                        "URL.host": url.getHost(),
                        "URL.file": url.getFile(),
                        "xpath": "/html"
                ])

Not very tidy. I want the URL-related fragments should be hidden in a "Builder" logic.

                        "URL": url.toExternalForm(),
                        "URL.host": url.getHost(),
                        "URL.file": url.getFile(),

slf4j is not necessary

build.gradle has

dependencies {

    // https://mvnrepository.com/artifact/org.slf4j/slf4j-api
    implementation group: 'org.slf4j', name: 'slf4j-api', version: "${slf4jVersion}"
    // https://mvnrepository.com/artifact/org.slf4j/slf4j-api
    implementation group: 'org.slf4j', name: 'slf4j-api', version: "${slf4jVersion}"
    // https://mvnrepository.com/artifact/org.slf4j/slf4j-simple
    testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: "${slf4jVersion}"

but actually slf4j is not used

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.