Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++ migration: port watch stream-related part of FSTRemoteStore #2331

Merged
merged 111 commits into from
Feb 3, 2019
Merged
Show file tree
Hide file tree
Changes from 105 commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
76b206b
Remove extra imports and includes
var-const Jan 15, 2019
7dcb9ae
Port
var-const Jan 15, 2019
88ba1a7
Forgotten method
var-const Jan 15, 2019
895bf45
style.sh
var-const Jan 15, 2019
b66c5d2
Forgotten the main part!
var-const Jan 15, 2019
3f5fdae
Fixup unit tests
var-const Jan 15, 2019
5ce04b8
Wip
var-const Jan 16, 2019
3dc3170
Step back
var-const Jan 18, 2019
9d1f931
Core compiles
var-const Jan 18, 2019
7ea798e
Merge branch 'master' into varconst/fst-watch-change
var-const Jan 18, 2019
84842be
quick fix
var-const Jan 18, 2019
e5132be
Test helpers
var-const Jan 18, 2019
6c85090
MockDatastore
var-const Jan 18, 2019
f7a90b5
FSTLocalStoreTests
var-const Jan 18, 2019
267963f
wip
var-const Jan 18, 2019
60a5726
wip
var-const Jan 18, 2019
dd9d98d
FSTRemoveEventTests
var-const Jan 18, 2019
aec3f54
FSTSerializerBetaTests
var-const Jan 19, 2019
b012929
wip
var-const Jan 19, 2019
1da0672
wip
var-const Jan 21, 2019
e0f77f2
wip
var-const Jan 21, 2019
df5cd00
wip
var-const Jan 21, 2019
5d3313a
wip
var-const Jan 21, 2019
31e5f3d
wip
var-const Jan 21, 2019
0637243
Ship it!
var-const Jan 21, 2019
26b39c7
use changes
var-const Jan 21, 2019
2cab650
quick fix
var-const Jan 21, 2019
ca88d8c
Unit tests pass
var-const Jan 21, 2019
2c0a7ef
style.sh
var-const Jan 21, 2019
52b131d
Small fixes
var-const Jan 21, 2019
35ce8cc
Get rid of ids alias
var-const Jan 21, 2019
8230fbb
Fixes
var-const Jan 21, 2019
ceb3bee
Move the test
var-const Jan 21, 2019
dd617de
Restore nooutstandingresponses variable
var-const Jan 21, 2019
9d80eea
Small fixes
var-const Jan 21, 2019
836d4de
style.sh
var-const Jan 21, 2019
134acaf
Review feedback
var-const Jan 22, 2019
f8dd5a5
Review feedback
var-const Jan 22, 2019
c02df53
comment
var-const Jan 22, 2019
ce17913
Appease linter
var-const Jan 22, 2019
a87cf7e
Initial
var-const Jan 22, 2019
d6a137a
wip
var-const Jan 23, 2019
1c99679
wip
var-const Jan 23, 2019
b2e0dd1
Initial
var-const Jan 23, 2019
e076f25
style.sh
var-const Jan 23, 2019
359d938
Merge branch 'master' into varconst/fst-document-view-change-type
var-const Jan 23, 2019
4b7c6a7
forgotten file
var-const Jan 23, 2019
7164e2b
Merge branch 'varconst/fst-document-view-change-type' into varconst/f…
var-const Jan 23, 2019
48fbab6
pt. 1 compiles
var-const Jan 23, 2019
0325248
Wip
var-const Jan 23, 2019
acbc626
wip
var-const Jan 24, 2019
b178b00
Merge branch 'master' into varconst/fst-watch-change-aggregator
var-const Jan 24, 2019
78395d8
In theory, all methods implemented
var-const Jan 24, 2019
4fee491
wip
var-const Jan 24, 2019
626efec
Almost compiles
var-const Jan 24, 2019
66db96a
Merge branch 'master' into varconst/fst-watch-change-aggregator
var-const Jan 24, 2019
d5defb1
appease linter
var-const Jan 24, 2019
52b2171
Bring method order into compliance
var-const Jan 24, 2019
e5fcf94
Compliacne, pt.2
var-const Jan 24, 2019
7a48512
core compiles
var-const Jan 25, 2019
66d8af2
unit tests compile
var-const Jan 26, 2019
594a841
Fix unit test -- all pass
var-const Jan 26, 2019
77d9cd7
style.sh
var-const Jan 26, 2019
fdf05a7
Review feedback
var-const Jan 26, 2019
a910ccd
Merge branch 'varconst/fst-watch-change-aggregator' into varconst/fst…
var-const Jan 26, 2019
c71a541
undo formatting
var-const Jan 26, 2019
24a745e
fix
var-const Jan 26, 2019
142f08c
Merge branch 'varconst/fst-watch-change-aggregator' into varconst/fst…
var-const Jan 27, 2019
e402e8b
Merge branch 'master' into varconst/fst-target-change
var-const Jan 27, 2019
e3dd85e
Temp code
var-const Jan 27, 2019
1eb4096
Implementation
var-const Jan 28, 2019
4147c3d
Updating references wip (FSTRemoteEventsTests still to do)
var-const Jan 28, 2019
3b3b012
Unit tests compile
var-const Jan 28, 2019
f1140d9
Unit tests compile and pass
var-const Jan 28, 2019
7ee766f
style.sh
var-const Jan 28, 2019
f00494a
Minor improvements
var-const Jan 28, 2019
766e940
Review feedback
var-const Jan 29, 2019
68ced12
Merge branch 'varconst/fst-target-change' into varconst/fst-remote-ev…
var-const Jan 29, 2019
90450fd
Initial implementation
var-const Jan 29, 2019
ed7ee52
Updating references
var-const Jan 29, 2019
ab6d050
Merge branch 'master' into varconst/fst-remote-event2
var-const Jan 29, 2019
920a921
All compiles
var-const Jan 29, 2019
3d509ec
style.sh
var-const Jan 29, 2019
b2d8ce4
Minor consistency changes
var-const Jan 29, 2019
355ced0
Skeleton
var-const Jan 29, 2019
df4c82e
Pseudochange
var-const Jan 29, 2019
9b494ee
Undo pseudochange
var-const Jan 29, 2019
c5a82f9
unstyle
var-const Jan 29, 2019
65b6ae2
Review feedback
var-const Jan 30, 2019
055372e
Merge branch 'varconst/fst-remote-event2' into varconst/fst-online-st…
var-const Jan 30, 2019
9435faf
Merge branch 'master' into varconst/fst-online-state-tracker
var-const Jan 30, 2019
151f99d
Change extension
var-const Jan 30, 2019
06947a5
style
var-const Jan 30, 2019
26bb8a9
wip
var-const Jan 30, 2019
61f821e
Redundant
var-const Jan 30, 2019
448b1fc
wip
var-const Jan 30, 2019
a7d99ae
wip
var-const Jan 30, 2019
286850f
Merge branch 'varconst/fst-online-state-tracker' into varconst/fst-re…
var-const Jan 30, 2019
6119d54
core actually compiles
var-const Jan 30, 2019
8fa4e74
style.sh
var-const Jan 30, 2019
6702112
wip
var-const Jan 31, 2019
4ec169e
unit tests compile
var-const Jan 31, 2019
6db2ee9
style.sh
var-const Jan 31, 2019
463b652
accidental
var-const Jan 31, 2019
dba5e70
small fixes
var-const Jan 31, 2019
43c575a
Merge branch 'master' into varconst/fst-online-state-tracker
var-const Feb 1, 2019
3025ca3
Merge branch 'varconst/fst-online-state-tracker' into varconst/fst-re…
var-const Feb 1, 2019
882a137
Merge branch 'master' into varconst/fst-remote-store1
var-const Feb 1, 2019
318debc
Review feedback
var-const Feb 1, 2019
af88702
style.sh
var-const Feb 1, 2019
33da753
fix weird bug
var-const Feb 2, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Firestore/Example/Tests/Integration/FSTDatastoreTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
using firebase::firestore::model::DocumentKey;
using firebase::firestore::model::DocumentKeySet;
using firebase::firestore::model::Precondition;
using firebase::firestore::model::OnlineState;
using firebase::firestore::model::TargetId;
using firebase::firestore::remote::Datastore;
using firebase::firestore::remote::GrpcConnection;
Expand Down Expand Up @@ -186,7 +187,8 @@ - (void)setUp {

_remoteStore = [[FSTRemoteStore alloc] initWithLocalStore:_localStore
datastore:_datastore
workerQueue:_testWorkerQueue.get()];
workerQueue:_testWorkerQueue.get()
onlineStateHandler:[](OnlineState) {}];

_testWorkerQueue->Enqueue([=] { [_remoteStore start]; });
}
Expand Down
9 changes: 6 additions & 3 deletions Firestore/Example/Tests/Local/FSTLocalStoreTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#import <FirebaseFirestore/FIRTimestamp.h>
#import <XCTest/XCTest.h>

#include <vector>

#import "Firestore/Source/Core/FSTQuery.h"
#import "Firestore/Source/Local/FSTLocalWriteResult.h"
#import "Firestore/Source/Local/FSTPersistence.h"
Expand Down Expand Up @@ -51,6 +53,7 @@
using firebase::firestore::model::SnapshotVersion;
using firebase::firestore::model::TargetId;
using firebase::firestore::remote::RemoteEvent;
using firebase::firestore::remote::TestTargetMetadataProvider;
using firebase::firestore::remote::WatchChangeAggregator;
using firebase::firestore::remote::WatchTargetChange;
using firebase::firestore::remote::WatchTargetChangeState;
Expand Down Expand Up @@ -906,9 +909,9 @@ - (void)testPersistsResumeTokens {
NSData *resumeToken = FSTTestResumeTokenFromSnapshotVersion(1000);

WatchTargetChange watchChange{WatchTargetChangeState::Current, {targetID}, resumeToken};
WatchChangeAggregator aggregator{[FSTTestTargetMetadataProvider
providerWithSingleResultForKey:testutil::Key("foo/bar")
targets:{targetID}]};
auto metadataProvider = TestTargetMetadataProvider::CreateSingleResultProvider(
testutil::Key("foo/bar"), std::vector<TargetId>{targetID});
WatchChangeAggregator aggregator{&metadataProvider};
aggregator.HandleTargetChange(watchChange);
RemoteEvent remoteEvent = aggregator.CreateRemoteEvent(testutil::Version(1000));
[self applyRemoteEvent:remoteEvent];
Expand Down
15 changes: 7 additions & 8 deletions Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
using firebase::firestore::remote::ExistenceFilterWatchChange;
using firebase::firestore::remote::RemoteEvent;
using firebase::firestore::remote::TargetChange;
using firebase::firestore::remote::TestTargetMetadataProvider;
using firebase::firestore::remote::WatchChange;
using firebase::firestore::remote::WatchChangeAggregator;
using firebase::firestore::remote::WatchTargetChange;
Expand Down Expand Up @@ -92,13 +93,12 @@ @interface FSTRemoteEventTests : XCTestCase

@implementation FSTRemoteEventTests {
NSData *_resumeToken1;
FSTTestTargetMetadataProvider *_targetMetadataProvider;
TestTargetMetadataProvider _targetMetadataProvider;
std::unordered_map<TargetId, int> _noOutstandingResponses;
}

- (void)setUp {
_resumeToken1 = [@"resume1" dataUsingEncoding:NSUTF8StringEncoding];
_targetMetadataProvider = [FSTTestTargetMetadataProvider new];
}

/**
Expand Down Expand Up @@ -145,7 +145,7 @@ - (void)setUp {
* considered active, or `_noOutstandingResponses` if all targets are already active.
* @param existingKeys The set of documents that are considered synced with the test targets as
* part of a previous listen. To modify this set during test execution, invoke
* `[_targetMetadataProvider setSyncedKeys:forQueryData:]`.
* `_targetMetadataProvider.SetSyncedKeys()`.
* @param watchChanges The watch changes to apply before returning the aggregator. Supported
* changes are `DocumentWatchChange` and `WatchTargetChange`.
*/
Expand All @@ -154,15 +154,15 @@ - (void)setUp {
outstandingResponses:(const std::unordered_map<TargetId, int> &)outstandingResponses
existingKeys:(DocumentKeySet)existingKeys
changes:(const std::vector<std::unique_ptr<WatchChange>> &)watchChanges {
WatchChangeAggregator aggregator{_targetMetadataProvider};
WatchChangeAggregator aggregator{&_targetMetadataProvider};

std::vector<TargetId> targetIDs;
for (const auto &kv : targetMap) {
TargetId targetID = kv.first;
FSTQueryData *queryData = kv.second;

targetIDs.push_back(targetID);
[_targetMetadataProvider setSyncedKeys:existingKeys forQueryData:queryData];
_targetMetadataProvider.SetSyncedKeys(existingKeys, queryData);
};

for (const auto &kv : outstandingResponses) {
Expand Down Expand Up @@ -223,7 +223,7 @@ - (void)setUp {

- (void)testWillAccumulateDocumentAddedAndRemovedEvents {
// The target map that contains an entry for every target in this test. If a target ID is
// omitted, the target is considered inactive and FSTTestTargetMetadataProvider will fail on
// omitted, the target is considered inactive and `TestTargetMetadataProvider` will fail on
// access.
std::unordered_map<TargetId, FSTQueryData *> targetMap{
[self queryDataForTargets:{1, 2, 3, 4, 5, 6}]};
Expand Down Expand Up @@ -614,8 +614,7 @@ - (void)testDocumentUpdate {
XCTAssertEqualObjects(event.document_updates().at(doc1.key), doc1);
XCTAssertEqualObjects(event.document_updates().at(doc2.key), doc2);

[_targetMetadataProvider setSyncedKeys:DocumentKeySet{doc1.key, doc2.key}
forQueryData:targetMap[1]];
_targetMetadataProvider.SetSyncedKeys(DocumentKeySet{doc1.key, doc2.key}, targetMap[1]);

FSTDeletedDocument *deletedDoc1 = [FSTDeletedDocument documentWithKey:doc1.key
version:testutil::Version(3)
Expand Down
2 changes: 1 addition & 1 deletion Firestore/Example/Tests/SpecTests/FSTMockDatastore.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class MockDatastore : public Datastore {
util::AsyncQueue* worker_queue,
auth::CredentialsProvider* credentials);

std::shared_ptr<WatchStream> CreateWatchStream(id<FSTWatchStreamDelegate> delegate) override;
std::shared_ptr<WatchStream> CreateWatchStream(WatchStreamCallback* callback) override;
std::shared_ptr<WriteStream> CreateWriteStream(id<FSTWriteStreamDelegate> delegate) override;

/**
Expand Down
18 changes: 9 additions & 9 deletions Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@
CredentialsProvider* credentials_provider,
FSTSerializerBeta* serializer,
GrpcConnection* grpc_connection,
id<FSTWatchStreamDelegate> delegate,
WatchStreamCallback* callback,
MockDatastore* datastore)
: WatchStream{worker_queue, credentials_provider, serializer, grpc_connection, delegate},
: WatchStream{worker_queue, credentials_provider, serializer, grpc_connection, callback},
datastore_{datastore},
delegate_{delegate} {
callback_{callback} {
}

const std::unordered_map<TargetId, FSTQueryData*>& ActiveTargets() const {
Expand All @@ -84,7 +84,7 @@
void Start() override {
HARD_ASSERT(!open_, "Trying to start already started watch stream");
open_ = true;
[delegate_ watchStreamDidOpen];
callback_->OnWatchStreamOpen();
}

void Stop() override {
Expand Down Expand Up @@ -118,7 +118,7 @@ void UnwatchTargetId(model::TargetId target_id) override {

void FailStream(const Status& error) {
open_ = false;
[delegate_ watchStreamWasInterruptedWithError:error];
callback_->OnWatchStreamClose(error);
}

void WriteWatchChange(const WatchChange& change, SnapshotVersion snap) {
Expand All @@ -145,14 +145,14 @@ void WriteWatchChange(const WatchChange& change, SnapshotVersion snap) {
}
}

[delegate_ watchStreamDidChange:change snapshotVersion:snap];
callback_->OnWatchStreamChange(change, snap);
}

private:
bool open_ = false;
std::unordered_map<TargetId, FSTQueryData*> active_targets_;
MockDatastore* datastore_ = nullptr;
id<FSTWatchStreamDelegate> delegate_ = nullptr;
WatchStreamCallback* callback_ = nullptr;
};

class MockWriteStream : public WriteStream {
Expand Down Expand Up @@ -248,11 +248,11 @@ int sent_mutations_count() const {
credentials_{credentials} {
}

std::shared_ptr<WatchStream> MockDatastore::CreateWatchStream(id<FSTWatchStreamDelegate> delegate) {
std::shared_ptr<WatchStream> MockDatastore::CreateWatchStream(WatchStreamCallback* callback) {
watch_stream_ = std::make_shared<MockWatchStream>(
worker_queue_, credentials_,
[[FSTSerializerBeta alloc] initWithDatabaseID:&database_info_->database_id()],
grpc_connection(), delegate, this);
grpc_connection(), callback, this);

return watch_stream_;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ typedef std::unordered_map<firebase::firestore::auth::User,
*
* Each method on the driver injects a different event into the system.
*/
@interface FSTSyncEngineTestDriver : NSObject <FSTOnlineStateDelegate>
@interface FSTSyncEngineTestDriver : NSObject

/**
* Initializes the underlying FSTSyncEngine with the given local persistence implementation and
Expand Down
18 changes: 8 additions & 10 deletions Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm
Original file line number Diff line number Diff line change
Expand Up @@ -153,18 +153,21 @@ - (instancetype)initWithPersistence:(id<FSTPersistence>)persistence

_datastore =
std::make_shared<MockDatastore>(_databaseInfo, _workerQueue.get(), &_credentialProvider);
_remoteStore = [[FSTRemoteStore alloc] initWithLocalStore:_localStore
datastore:_datastore
workerQueue:_workerQueue.get()];
_remoteStore =
[[FSTRemoteStore alloc] initWithLocalStore:_localStore
datastore:_datastore
workerQueue:_workerQueue.get()
onlineStateHandler:[self](OnlineState onlineState) {
[self.syncEngine applyChangedOnlineState:onlineState];
[self.eventManager applyChangedOnlineState:onlineState];
}];

_syncEngine = [[FSTSyncEngine alloc] initWithLocalStore:_localStore
remoteStore:_remoteStore
initialUser:initialUser];
_remoteStore.syncEngine = _syncEngine;
_eventManager = [FSTEventManager eventManagerWithSyncEngine:_syncEngine];

_remoteStore.onlineStateDelegate = self;

// Set up internal event tracking for the spec tests.
NSMutableArray<FSTQueryEvent *> *events = [NSMutableArray array];
_eventHandler = ^(FSTQueryEvent *e) {
Expand Down Expand Up @@ -203,11 +206,6 @@ - (void)drainQueue {
return _currentUser;
}

- (void)applyChangedOnlineState:(OnlineState)onlineState {
[self.syncEngine applyChangedOnlineState:onlineState];
[self.eventManager applyChangedOnlineState:onlineState];
}

- (void)start {
_workerQueue->EnqueueBlocking([&] {
[self.localStore start];
Expand Down
89 changes: 48 additions & 41 deletions Firestore/Example/Tests/Util/FSTHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#import <Foundation/Foundation.h>

#include <string>
#include <unordered_map>
#include <vector>

#import "Firestore/Source/Model/FSTDocument.h"
Expand Down Expand Up @@ -141,51 +142,57 @@ inline NSString *FSTRemoveExceptionPrefix(NSString *exception) {
} while (0)

/**
* An implementation of FSTTargetMetadataProvider that provides controlled access to the
* `FSTTargetMetadataProvider` callbacks. Any target accessed via these callbacks must be
* An implementation of `TargetMetadataProvider` that provides controlled access to the
* `TargetMetadataProvider` callbacks. Any target accessed via these callbacks must be
* registered beforehand via the factory methods or via `setSyncedKeys:forQueryData:`.
*/
@interface FSTTestTargetMetadataProvider : NSObject <FSTTargetMetadataProvider>

/**
* Creates an FSTTestTargetMetadataProvider that behaves as if there's an established listen for
* each of the given targets, where each target has previously seen query results containing just
* the given documentKey.
*
* Internally this means that the `remoteKeysForTarget` callback for these targets will return just
* the documentKey and that the provided targets will be returned as active from the
* `queryDataForTarget` target.
*/
+ (instancetype)
providerWithSingleResultForKey:(firebase::firestore::model::DocumentKey)documentKey
targets:
(const std::vector<firebase::firestore::model::TargetId> &)targets;

+ (instancetype)
providerWithSingleResultForKey:(firebase::firestore::model::DocumentKey)documentKey
listenTargets:
(const std::vector<firebase::firestore::model::TargetId> &)listenTargets
limboTargets:
(const std::vector<firebase::firestore::model::TargetId> &)limboTargets;

/**
* Creates an FSTTestTargetMetadataProvider that behaves as if there's an established listen for
* each of the given targets, where each target has not seen any previous document.
*
* Internally this means that the `remoteKeysForTarget` callback for these targets will return an
* empty set of document keys and that the provided targets will be returned as active from the
* `queryDataForTarget` target.
*/
+ (instancetype)
providerWithEmptyResultForKey:(firebase::firestore::model::DocumentKey)documentKey
targets:
(const std::vector<firebase::firestore::model::TargetId> &)targets;
namespace firebase {
namespace firestore {
namespace remote {

/** Sets or replaces the local state for the provided query data. */
- (void)setSyncedKeys:(firebase::firestore::model::DocumentKeySet)keys
forQueryData:(FSTQueryData *)queryData;
class TestTargetMetadataProvider : public TargetMetadataProvider {
public:
/**
* Creates a `TestTargetMetadataProvider` that behaves as if there's an established listen for
* each of the given targets, where each target has previously seen query results containing just
* the given `document_key`.
*
* Internally this means that the `GetRemoteKeysForTarget` callback for these targets will return
* just the `document_key` and that the provided targets will be returned as active from the
* `GetQueryDataForTarget` target.
*/
static TestTargetMetadataProvider CreateSingleResultProvider(
model::DocumentKey document_key, const std::vector<model::TargetId> &targets);
static TestTargetMetadataProvider CreateSingleResultProvider(
model::DocumentKey document_key,
const std::vector<model::TargetId> &targets,
const std::vector<model::TargetId> &limbo_targets);

/**
* Creates an `TestTargetMetadataProvider` that behaves as if there's an established listen for
* each of the given targets, where each target has not seen any previous document.
*
* Internally this means that the `GetRemoteKeysForTarget` callback for these targets will return
* an empty set of document keys and that the provided targets will be returned as active from the
* `GetQueryDataForTarget` target.
*/
static TestTargetMetadataProvider CreateEmptyResultProvider(
const model::DocumentKey &document_key, const std::vector<model::TargetId> &targets);

/** Sets or replaces the local state for the provided query data. */
void SetSyncedKeys(model::DocumentKeySet keys, FSTQueryData *query_data);

model::DocumentKeySet GetRemoteKeysForTarget(model::TargetId target_id) const override;
FSTQueryData *GetQueryDataForTarget(model::TargetId target_id) const override;

private:
std::unordered_map<model::TargetId, model::DocumentKeySet> synced_keys_;
std::unordered_map<model::TargetId, FSTQueryData *> query_data_;
};

@end
} // namespace remote
} // namespace firestore
} // namespace firebase

/** Creates a new FIRTimestamp from components. Note that year, month, and day are all one-based. */
FIRTimestamp *FSTTestTimestamp(int year, int month, int day, int hour, int minute, int second);
Expand Down
Loading